From 9e25e0ed4b0b889cbf1d432415f0c3860399bd27 Mon Sep 17 00:00:00 2001 From: Jakob Hasse Date: Tue, 22 Mar 2022 16:20:51 +0800 Subject: [PATCH] refactor (cxx): changed cxx unit tests to component unit tests --- components/cxx/test/CMakeLists.txt | 3 - components/cxx/test/test_stack_check_cxx.cpp | 30 ---- .../cxx/test_apps/exception/CMakeLists.txt | 8 + components/cxx/test_apps/exception/README.md | 2 + .../test_apps/exception/main/CMakeLists.txt | 3 + .../exception/main/test_exception.cpp} | 167 +++--------------- .../exception/pytest_cxx_exception.py | 14 ++ .../test_apps/exception/sdkconfig.defaults | 12 ++ .../exception_no_except/CMakeLists.txt | 8 + .../test_apps/exception_no_except/README.md | 2 + .../exception_no_except/main/CMakeLists.txt | 3 + .../main/test_exception_no_except.cpp | 38 ++++ .../pytest_cxx_exception_no_except.py | 25 +++ .../exception_no_except/sdkconfig.defaults | 12 ++ .../cxx/test_apps/general/CMakeLists.txt | 8 + components/cxx/test_apps/general/README.md | 2 + .../cxx/test_apps/general/main/CMakeLists.txt | 2 + .../general/main/test_cxx_general.cpp} | 146 +++++++++++++-- .../test_apps/general/pytest_cxx_general.py | 42 +++++ .../general/sdkconfig.ci.exceptions_rtti | 3 - .../test_apps/general/sdkconfig.ci.noexcept | 0 .../cxx/test_apps/general/sdkconfig.defaults | 10 ++ components/cxx/test_apps/rtti/CMakeLists.txt | 8 + components/cxx/test_apps/rtti/README.md | 2 + .../cxx/test_apps/rtti/main/CMakeLists.txt | 3 + .../rtti/main}/test_rtti.cpp | 41 ++++- .../cxx/test_apps/rtti/pytest_cxx_rtti.py | 14 ++ .../cxx/test_apps/rtti/sdkconfig.defaults | 13 ++ tools/unit-test-app/configs/cxx_exceptions | 5 - tools/unit-test-app/configs/cxx_exceptions_c3 | 5 - tools/unit-test-app/configs/cxx_rtti_c3 | 6 - 31 files changed, 431 insertions(+), 206 deletions(-) delete mode 100644 components/cxx/test/CMakeLists.txt delete mode 100644 components/cxx/test/test_stack_check_cxx.cpp create mode 100644 components/cxx/test_apps/exception/CMakeLists.txt create mode 100644 components/cxx/test_apps/exception/README.md create mode 100644 components/cxx/test_apps/exception/main/CMakeLists.txt rename components/cxx/{test/test_cxx.cpp => test_apps/exception/main/test_exception.cpp} (50%) create mode 100644 components/cxx/test_apps/exception/pytest_cxx_exception.py create mode 100644 components/cxx/test_apps/exception/sdkconfig.defaults create mode 100644 components/cxx/test_apps/exception_no_except/CMakeLists.txt create mode 100644 components/cxx/test_apps/exception_no_except/README.md create mode 100644 components/cxx/test_apps/exception_no_except/main/CMakeLists.txt create mode 100644 components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp create mode 100644 components/cxx/test_apps/exception_no_except/pytest_cxx_exception_no_except.py create mode 100644 components/cxx/test_apps/exception_no_except/sdkconfig.defaults create mode 100644 components/cxx/test_apps/general/CMakeLists.txt create mode 100644 components/cxx/test_apps/general/README.md create mode 100644 components/cxx/test_apps/general/main/CMakeLists.txt rename components/cxx/{test/test_initialization.cpp => test_apps/general/main/test_cxx_general.cpp} (61%) create mode 100644 components/cxx/test_apps/general/pytest_cxx_general.py rename tools/unit-test-app/configs/cxx_rtti => components/cxx/test_apps/general/sdkconfig.ci.exceptions_rtti (52%) create mode 100644 components/cxx/test_apps/general/sdkconfig.ci.noexcept create mode 100644 components/cxx/test_apps/general/sdkconfig.defaults create mode 100644 components/cxx/test_apps/rtti/CMakeLists.txt create mode 100644 components/cxx/test_apps/rtti/README.md create mode 100644 components/cxx/test_apps/rtti/main/CMakeLists.txt rename components/cxx/{test => test_apps/rtti/main}/test_rtti.cpp (61%) create mode 100644 components/cxx/test_apps/rtti/pytest_cxx_rtti.py create mode 100644 components/cxx/test_apps/rtti/sdkconfig.defaults delete mode 100644 tools/unit-test-app/configs/cxx_exceptions delete mode 100644 tools/unit-test-app/configs/cxx_exceptions_c3 delete mode 100644 tools/unit-test-app/configs/cxx_rtti_c3 diff --git a/components/cxx/test/CMakeLists.txt b/components/cxx/test/CMakeLists.txt deleted file mode 100644 index dc24ac276d..0000000000 --- a/components/cxx/test/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRC_DIRS "." - PRIV_INCLUDE_DIRS "." - PRIV_REQUIRES cmock driver) diff --git a/components/cxx/test/test_stack_check_cxx.cpp b/components/cxx/test/test_stack_check_cxx.cpp deleted file mode 100644 index 86f179be28..0000000000 --- a/components/cxx/test/test_stack_check_cxx.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include "unity.h" - -#if CONFIG_COMPILER_STACK_CHECK - -static void recur_and_smash_cxx() -{ - static int cnt; - volatile uint8_t buf[50]; - volatile int num = sizeof(buf)+10; - - if (cnt++ < 1) { - recur_and_smash_cxx(); - } - for (int i = 0; i < num; i++) { - buf[i] = 0; - } -} - - -TEST_CASE("stack smashing protection CXX", "[stack_check] [ignore]") -{ - recur_and_smash_cxx(); -} - -#endif diff --git a/components/cxx/test_apps/exception/CMakeLists.txt b/components/cxx/test_apps/exception/CMakeLists.txt new file mode 100644 index 0000000000..236f0449e6 --- /dev/null +++ b/components/cxx/test_apps/exception/CMakeLists.txt @@ -0,0 +1,8 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_cxx_exception) diff --git a/components/cxx/test_apps/exception/README.md b/components/cxx/test_apps/exception/README.md new file mode 100644 index 0000000000..1fb88efd15 --- /dev/null +++ b/components/cxx/test_apps/exception/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C3 | +| ----------------- | ----- | -------- | diff --git a/components/cxx/test_apps/exception/main/CMakeLists.txt b/components/cxx/test_apps/exception/main/CMakeLists.txt new file mode 100644 index 0000000000..dd12b0365f --- /dev/null +++ b/components/cxx/test_apps/exception/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_exception.cpp" + REQUIRES test_utils + PRIV_REQUIRES unity driver) diff --git a/components/cxx/test/test_cxx.cpp b/components/cxx/test_apps/exception/main/test_exception.cpp similarity index 50% rename from components/cxx/test/test_cxx.cpp rename to components/cxx/test_apps/exception/main/test_exception.cpp index 722c5754d0..69f7102f07 100644 --- a/components/cxx/test/test_cxx.cpp +++ b/components/cxx/test_apps/exception/main/test_exception.cpp @@ -3,51 +3,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include -#include #include -#include #include "unity.h" -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "soc/soc.h" - -TEST_CASE("can use new and delete", "[cxx]") -{ - int* int_p = new int(10); - delete int_p; - int* int_array = new int[10]; - delete[] int_array; -} - -class Base -{ -public: - virtual ~Base() {} - virtual void foo() = 0; -}; - -class Derived : public Base -{ -public: - virtual void foo() { } -}; - -TEST_CASE("can call virtual functions", "[cxx]") -{ - Derived d; - Base& b = static_cast(d); - b.foo(); -} - -TEST_CASE("can use std::vector", "[cxx]") -{ - std::vector v(10, 1); - v[0] = 42; - TEST_ASSERT_EQUAL(51, std::accumulate(std::begin(v), std::end(v), 0)); -} +#include "memory_checks.h" /* Note: When first exception (in system) is thrown this test produces memory leaks report (~300 bytes): - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals @@ -55,19 +13,27 @@ TEST_CASE("can use std::vector", "[cxx]") - 88 bytes are allocated by pthread_setspecific() to init internal lock - some more memory... */ -#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS - #if CONFIG_IDF_TARGET_ESP32 -#define LEAKS "300" +#define LEAKS (300) #elif CONFIG_IDF_TARGET_ESP32S2 -#define LEAKS "800" +#define LEAKS (800) #elif CONFIG_IDF_TARGET_ESP32C3 -#define LEAKS "700" +#define LEAKS (700) #else #error "unknown target in CXX tests, can't set leaks threshold" #endif -TEST_CASE("c++ exceptions work", "[cxx] [exceptions] [leaks=" LEAKS "]") +extern "C" void setUp() +{ + test_utils_record_free_mem(); +} + +extern "C" void tearDown() +{ + test_utils_finish_and_evaluate_leaks(LEAKS, LEAKS); +} + +TEST_CASE("c++ exceptions work", "[cxx] [exceptions]") { int thrown_value; try { @@ -76,10 +42,9 @@ TEST_CASE("c++ exceptions work", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL(20, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ bool exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ bool exception", "[cxx] [exceptions]") { bool thrown_value = false; try { @@ -88,10 +53,9 @@ TEST_CASE("c++ bool exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL(true, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ void exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ void exception", "[cxx] [exceptions]") { void* thrown_value = 0; try { @@ -100,10 +64,9 @@ TEST_CASE("c++ void exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL(47, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ uint64_t exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ uint64_t exception", "[cxx] [exceptions]") { uint64_t thrown_value = 0; try { @@ -112,10 +75,9 @@ TEST_CASE("c++ uint64_t exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL(47, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ char exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ char exception", "[cxx] [exceptions]") { char thrown_value = '0'; try { @@ -124,10 +86,9 @@ TEST_CASE("c++ char exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL('/', thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ wchar exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ wchar exception", "[cxx] [exceptions]") { wchar_t thrown_value = 0; try { @@ -136,10 +97,9 @@ TEST_CASE("c++ wchar exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL((wchar_t) 47, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ float exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ float exception", "[cxx] [exceptions]") { float thrown_value = 0; try { @@ -148,10 +108,9 @@ TEST_CASE("c++ float exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL(23.5, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ double exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ double exception", "[cxx] [exceptions]") { double thrown_value = 0; try { @@ -160,10 +119,9 @@ TEST_CASE("c++ double exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL(23.5d, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ const char* exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ const char* exception", "[cxx] [exceptions]") { const char *thrown_value = 0; try { @@ -172,7 +130,6 @@ TEST_CASE("c++ const char* exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e; } TEST_ASSERT_EQUAL_STRING("Hi :)", thrown_value); - printf("OK?\n"); } struct NonExcTypeThrowee { @@ -181,7 +138,7 @@ public: NonExcTypeThrowee(int value) : value(value) { } }; -TEST_CASE("c++ any class exception", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ any class exception", "[cxx] [exceptions]") { int thrown_value = 0; try { @@ -190,7 +147,6 @@ TEST_CASE("c++ any class exception", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e.value; } TEST_ASSERT_EQUAL(47, thrown_value); - printf("OK?\n"); } struct ExcTypeThrowee : public std::exception { @@ -199,7 +155,7 @@ public: ExcTypeThrowee(int value) : value(value) { } }; -TEST_CASE("c++ std::exception child", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ std::exception child", "[cxx] [exceptions]") { int thrown_value = 0; try { @@ -208,10 +164,9 @@ TEST_CASE("c++ std::exception child", "[cxx] [exceptions] [leaks=" LEAKS "]") thrown_value = e.value; } TEST_ASSERT_EQUAL(47, thrown_value); - printf("OK?\n"); } -TEST_CASE("c++ exceptions emergency pool", "[cxx] [exceptions] [leaks=" LEAKS "]") +TEST_CASE("c++ exceptions emergency pool", "[cxx] [exceptions]") { void **p, **pprev = NULL; int thrown_value = 0; @@ -257,72 +212,8 @@ TEST_CASE("c++ exceptions emergency pool", "[cxx] [exceptions] [leaks=" LEAKS "] #endif } -#else // !CONFIG_COMPILER_CXX_EXCEPTIONS - -TEST_CASE("std::out_of_range exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]") +extern "C" void app_main(void) { - std::vector v(10); - v.at(20) = 42; - TEST_FAIL_MESSAGE("Unreachable because we are aborted on the line above"); + printf("CXX EXCEPTION TEST\n"); + unity_run_menu(); } - -TEST_CASE("std::bad_alloc exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]") -{ - std::string s = std::string(2000000000, 'a'); - (void)s; - TEST_FAIL_MESSAGE("Unreachable because we are aborted on the line above"); -} - -#endif - -/* These test cases pull a lot of code from libstdc++ and are disabled for now - */ -#if 0 -#include -#include - -TEST_CASE("can use iostreams", "[cxx]") -{ - std::cout << "hello world"; -} - -TEST_CASE("can call std::function and bind", "[cxx]") -{ - int outer = 1; - std::function fn = [&outer](int x) -> int { - return x + outer; - }; - outer = 5; - TEST_ASSERT_EQUAL(6, fn(1)); - - auto bound = std::bind(fn, outer); - outer = 10; - TEST_ASSERT_EQUAL(15, bound()); -} - -#endif - -/* Tests below are done in the compile time, don't actually get run. */ -/* Check whether a enumerator flag can be used in C++ */ - - -template __attribute__((unused)) static void test_binary_operators() -{ - T flag1 = (T)0; - T flag2 = (T)0; - flag1 = ~flag1; - flag1 = flag1 | flag2; - flag1 = flag1 & flag2; - flag1 = flag1 ^ flag2; - flag1 = flag1 >> 2; - flag1 = flag1 << 2; - flag1 |= flag2; - flag1 &= flag2; - flag1 ^= flag2; - flag1 >>= 2; - flag1 <<= 2; -} - -//Add more types here. If any flags cannot pass the build, use FLAG_ATTR in esp_attr.h -#include "driver/timer_types_legacy.h" -template void test_binary_operators(); diff --git a/components/cxx/test_apps/exception/pytest_cxx_exception.py b/components/cxx/test_apps/exception/pytest_cxx_exception.py new file mode 100644 index 0000000000..497afc4cc5 --- /dev/null +++ b/components/cxx/test_apps/exception/pytest_cxx_exception.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_cxx_exception(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('*') + dut.expect_unity_test_output() diff --git a/components/cxx/test_apps/exception/sdkconfig.defaults b/components/cxx/test_apps/exception/sdkconfig.defaults new file mode 100644 index 0000000000..198337030a --- /dev/null +++ b/components/cxx/test_apps/exception/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_ESP_TASK_WDT=n +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3000 diff --git a/components/cxx/test_apps/exception_no_except/CMakeLists.txt b/components/cxx/test_apps/exception_no_except/CMakeLists.txt new file mode 100644 index 0000000000..236f0449e6 --- /dev/null +++ b/components/cxx/test_apps/exception_no_except/CMakeLists.txt @@ -0,0 +1,8 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_cxx_exception) diff --git a/components/cxx/test_apps/exception_no_except/README.md b/components/cxx/test_apps/exception_no_except/README.md new file mode 100644 index 0000000000..1fb88efd15 --- /dev/null +++ b/components/cxx/test_apps/exception_no_except/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C3 | +| ----------------- | ----- | -------- | diff --git a/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt b/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt new file mode 100644 index 0000000000..aa767eafcd --- /dev/null +++ b/components/cxx/test_apps/exception_no_except/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_exception_no_except.cpp" + REQUIRES test_utils + PRIV_REQUIRES unity driver) diff --git a/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp b/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp new file mode 100644 index 0000000000..954e73a4df --- /dev/null +++ b/components/cxx/test_apps/exception_no_except/main/test_exception_no_except.cpp @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "memory_checks.h" + +extern "C" void setUp() +{ +} + +extern "C" void tearDown() +{ +} + +TEST_CASE("std::out_of_range exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]") +{ + std::vector v(10); + v.at(20) = 42; + TEST_FAIL_MESSAGE("Unreachable because we are aborted on the line above"); +} + +TEST_CASE("std::bad_alloc exception when -fno-exceptions", "[cxx][reset=abort,SW_CPU_RESET]") +{ + std::string s = std::string(2000000000, 'a'); + (void)s; + TEST_FAIL_MESSAGE("Unreachable because we are aborted on the line above"); +} + +extern "C" void app_main(void) +{ + printf("CXX NO EXCEPTION TEST\n"); + unity_run_menu(); +} diff --git a/components/cxx/test_apps/exception_no_except/pytest_cxx_exception_no_except.py b/components/cxx/test_apps/exception_no_except/pytest_cxx_exception_no_except.py new file mode 100644 index 0000000000..ee825df7a1 --- /dev/null +++ b/components/cxx/test_apps/exception_no_except/pytest_cxx_exception_no_except.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_cxx_noexcept_out_of_range(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('1') + dut.expect_exact('abort() was called') + dut.expect_exact('Rebooting...') + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_cxx_noexcept_bad_alloc(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('2') + dut.expect_exact('abort() was called') + dut.expect_exact('Rebooting...') diff --git a/components/cxx/test_apps/exception_no_except/sdkconfig.defaults b/components/cxx/test_apps/exception_no_except/sdkconfig.defaults new file mode 100644 index 0000000000..add1851e93 --- /dev/null +++ b/components/cxx/test_apps/exception_no_except/sdkconfig.defaults @@ -0,0 +1,12 @@ +CONFIG_COMPILER_CXX_EXCEPTIONS=n +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_ESP_TASK_WDT=n +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3000 diff --git a/components/cxx/test_apps/general/CMakeLists.txt b/components/cxx/test_apps/general/CMakeLists.txt new file mode 100644 index 0000000000..d37fad99f0 --- /dev/null +++ b/components/cxx/test_apps/general/CMakeLists.txt @@ -0,0 +1,8 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_cxx_general) diff --git a/components/cxx/test_apps/general/README.md b/components/cxx/test_apps/general/README.md new file mode 100644 index 0000000000..1fb88efd15 --- /dev/null +++ b/components/cxx/test_apps/general/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C3 | +| ----------------- | ----- | -------- | diff --git a/components/cxx/test_apps/general/main/CMakeLists.txt b/components/cxx/test_apps/general/main/CMakeLists.txt new file mode 100644 index 0000000000..271a3dd092 --- /dev/null +++ b/components/cxx/test_apps/general/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "test_cxx_general.cpp" + PRIV_REQUIRES unity test_utils driver) diff --git a/components/cxx/test/test_initialization.cpp b/components/cxx/test_apps/general/main/test_cxx_general.cpp similarity index 61% rename from components/cxx/test/test_initialization.cpp rename to components/cxx/test_apps/general/main/test_cxx_general.cpp index 3c0a2f5367..006ea56a46 100644 --- a/components/cxx/test/test_initialization.cpp +++ b/components/cxx/test_apps/general/main/test_cxx_general.cpp @@ -1,18 +1,29 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ + #include #include -#include -#include -#include "unity.h" -#include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" -#include "soc/soc.h" +#include "esp_log.h" +#include "unity.h" +#include "memory_checks.h" + +extern "C" void setUp() +{ + test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_record_free_mem(); +} + +extern "C" void tearDown() +{ + size_t leak_level = test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_finish_and_evaluate_leaks(leak_level, leak_level); +} static const char* TAG = "cxx"; @@ -31,8 +42,10 @@ static int non_pod_test_helper(int new_val) return ret; } -TEST_CASE("can use static initializers for non-POD types", "[cxx]") +// Will fail if run twice +TEST_CASE("can use static initializers for non-POD types", "[restart_init]") { + test_utils_set_leak_level(300, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); TEST_ASSERT_EQUAL(42, non_pod_test_helper(1)); TEST_ASSERT_EQUAL(1, non_pod_test_helper(0)); } @@ -84,8 +97,9 @@ static int start_slow_init_task(int id, int affinity) reinterpret_cast(id), 3, NULL, affinity) ? 1 : 0; } -TEST_CASE("static initialization guards work as expected", "[cxx]") +TEST_CASE("static initialization guards work as expected", "[misc]") { + test_utils_set_leak_level(300, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); s_slow_init_sem = xSemaphoreCreateCounting(10, 0); TEST_ASSERT_NOT_NULL(s_slow_init_sem); int task_count = 0; @@ -128,7 +142,7 @@ GlobalInitTest g_init_test1; GlobalInitTest g_init_test2; GlobalInitTest g_init_test3; -TEST_CASE("global initializers run in the correct order", "[cxx]") +TEST_CASE("global initializers run in the correct order", "[misc]") { TEST_ASSERT_EQUAL(0, g_init_test1.index); TEST_ASSERT_EQUAL(1, g_init_test2.index); @@ -158,7 +172,7 @@ StaticInitTestBeforeScheduler g_static_init_test1; StaticInitTestBeforeScheduler g_static_init_test2; StaticInitTestBeforeScheduler g_static_init_test3; -TEST_CASE("before scheduler has started, static initializers work correctly", "[cxx]") +TEST_CASE("before scheduler has started, static initializers work correctly", "[misc]") { TEST_ASSERT_EQUAL(1, g_static_init_test1.index); TEST_ASSERT_EQUAL(1, g_static_init_test2.index); @@ -190,9 +204,119 @@ PriorityInitTest g_static_init_priority_test2; PriorityInitTest g_static_init_priority_test1 __attribute__((init_priority(1000))); PriorityInitTest g_static_init_priority_test0 __attribute__((init_priority(999))); -TEST_CASE("init_priority extension works", "[cxx]") +TEST_CASE("init_priority extension works", "[misc]") { TEST_ASSERT_EQUAL(0, g_static_init_priority_test0.index); TEST_ASSERT_EQUAL(1, g_static_init_priority_test1.index); TEST_ASSERT_EQUAL(2, g_static_init_priority_test2.index); } + +TEST_CASE("can use new and delete", "[misc]") +{ + int* int_p = new int(10); + delete int_p; + int* int_array = new int[10]; + delete[] int_array; +} + +class Base +{ +public: + virtual ~Base() = default; + virtual void foo() = 0; +}; + +class Derived : public Base +{ +public: + virtual void foo() { } +}; + +TEST_CASE("can call virtual functions", "[misc]") +{ + Derived d; + Base& b = static_cast(d); + b.foo(); +} + +TEST_CASE("can use std::vector", "[misc]") +{ + std::vector v(10, 1); + v[0] = 42; + TEST_ASSERT_EQUAL(51, std::accumulate(std::begin(v), std::end(v), 0)); +} + +/* These test cases pull a lot of code from libstdc++ and are disabled for now + */ +#if 0 +#include +#include + +TEST_CASE("can use iostreams", "[misc]") +{ + std::cout << "hello world"; +} + +TEST_CASE("can call std::function and bind", "[misc]") +{ + int outer = 1; + std::function fn = [&outer](int x) -> int { + return x + outer; + }; + outer = 5; + TEST_ASSERT_EQUAL(6, fn(1)); + + auto bound = std::bind(fn, outer); + outer = 10; + TEST_ASSERT_EQUAL(15, bound()); +} + +#endif + +static void recur_and_smash_cxx() +{ + static int cnt; + volatile uint8_t buf[50]; + volatile int num = sizeof(buf)+10; + + if (cnt++ < 1) { + recur_and_smash_cxx(); + } + for (int i = 0; i < num; i++) { + buf[i] = 0; + } +} + +TEST_CASE("stack smashing protection CXX", "[stack_smash]") +{ + recur_and_smash_cxx(); +} + +extern "C" void app_main(void) +{ + printf("CXX GENERAL TEST\n"); + unity_run_menu(); +} + +/* Tests below are done in the compile time, don't actually get run. */ +/* Check whether a enumerator flag can be used in C++ */ +template __attribute__((unused)) static void test_binary_operators() +{ + T flag1 = (T)0; + T flag2 = (T)0; + flag1 = ~flag1; + flag1 = flag1 | flag2; + flag1 = flag1 & flag2; + flag1 = flag1 ^ flag2; + flag1 = flag1 >> 2; + flag1 = flag1 << 2; + flag1 |= flag2; + flag1 &= flag2; + flag1 ^= flag2; + flag1 >>= 2; + flag1 <<= 2; +} + +//Add more types here. If any flags cannot pass the build, use FLAG_ATTR in esp_attr.h +#include "driver/timer_types_legacy.h" +template void test_binary_operators(); diff --git a/components/cxx/test_apps/general/pytest_cxx_general.py b/components/cxx/test_apps/general/pytest_cxx_general.py new file mode 100644 index 0000000000..b3fe3ed0d8 --- /dev/null +++ b/components/cxx/test_apps/general/pytest_cxx_general.py @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + +configurations = [ + 'noexcept', + 'exceptions_rtti' +] + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +@pytest.mark.parametrize('config', configurations, indirect=True) +def test_cxx_static_init_non_pod(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('\"can use static initializers for non-POD types\"') + dut.expect_unity_test_output() + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +@pytest.mark.parametrize('config', configurations, indirect=True) +def test_cxx_misc(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[misc]') + dut.expect_unity_test_output() + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +@pytest.mark.parametrize('config', configurations, indirect=True) +def test_cxx_stack_smash(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('\"stack smashing protection CXX\"') + dut.expect_exact('Stack smashing protect failure!') + dut.expect_exact('abort() was called') + dut.expect_exact('Rebooting...') diff --git a/tools/unit-test-app/configs/cxx_rtti b/components/cxx/test_apps/general/sdkconfig.ci.exceptions_rtti similarity index 52% rename from tools/unit-test-app/configs/cxx_rtti rename to components/cxx/test_apps/general/sdkconfig.ci.exceptions_rtti index 4ec318f1d0..9cea76c463 100644 --- a/tools/unit-test-app/configs/cxx_rtti +++ b/components/cxx/test_apps/general/sdkconfig.ci.exceptions_rtti @@ -1,6 +1,3 @@ -# Only need to test this for one target (e.g. ESP32) -CONFIG_IDF_TARGET="esp32" -TEST_COMPONENTS=cxx CONFIG_COMPILER_CXX_EXCEPTIONS=y CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 CONFIG_COMPILER_CXX_RTTI=y diff --git a/components/cxx/test_apps/general/sdkconfig.ci.noexcept b/components/cxx/test_apps/general/sdkconfig.ci.noexcept new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/cxx/test_apps/general/sdkconfig.defaults b/components/cxx/test_apps/general/sdkconfig.defaults new file mode 100644 index 0000000000..beea275c4e --- /dev/null +++ b/components/cxx/test_apps/general/sdkconfig.defaults @@ -0,0 +1,10 @@ +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_ESP_TASK_WDT=n +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3000 diff --git a/components/cxx/test_apps/rtti/CMakeLists.txt b/components/cxx/test_apps/rtti/CMakeLists.txt new file mode 100644 index 0000000000..f8b0144caf --- /dev/null +++ b/components/cxx/test_apps/rtti/CMakeLists.txt @@ -0,0 +1,8 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_cxx_rtti) diff --git a/components/cxx/test_apps/rtti/README.md b/components/cxx/test_apps/rtti/README.md new file mode 100644 index 0000000000..1fb88efd15 --- /dev/null +++ b/components/cxx/test_apps/rtti/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C3 | +| ----------------- | ----- | -------- | diff --git a/components/cxx/test_apps/rtti/main/CMakeLists.txt b/components/cxx/test_apps/rtti/main/CMakeLists.txt new file mode 100644 index 0000000000..74d9dc9108 --- /dev/null +++ b/components/cxx/test_apps/rtti/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_rtti.cpp" + REQUIRES test_utils + PRIV_REQUIRES unity driver) diff --git a/components/cxx/test/test_rtti.cpp b/components/cxx/test_apps/rtti/main/test_rtti.cpp similarity index 61% rename from components/cxx/test/test_rtti.cpp rename to components/cxx/test_apps/rtti/main/test_rtti.cpp index 252b9ad872..1490b8dc6c 100644 --- a/components/cxx/test/test_rtti.cpp +++ b/components/cxx/test_apps/rtti/main/test_rtti.cpp @@ -1,12 +1,40 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include "unity.h" +#include "memory_checks.h" -#ifdef CONFIG_COMPILER_CXX_RTTI +/* Note: When first exception (in system) is thrown this test produces memory leaks report (~300 bytes): + - 8 bytes are allocated by __cxa_get_globals() to keep __cxa_eh_globals + - 16 bytes are allocated by pthread_setspecific() which is called by __cxa_get_globals() to init TLS var for __cxa_eh_globals + - 88 bytes are allocated by pthread_setspecific() to init internal lock + - some more memory... + */ +#if CONFIG_IDF_TARGET_ESP32 +#define LEAKS (300) +#elif CONFIG_IDF_TARGET_ESP32S2 +#define LEAKS (800) +#elif CONFIG_IDF_TARGET_ESP32C3 +#define LEAKS (700) +#else +#error "unknown target in CXX tests, can't set leaks threshold" +#endif + +extern "C" void setUp() +{ + test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_record_free_mem(); +} + +extern "C" void tearDown() +{ + size_t leak_level = test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); + test_utils_finish_and_evaluate_leaks(leak_level, leak_level); +} using namespace std; @@ -67,9 +95,9 @@ TEST_CASE("typeid of function works", "[cxx]") TEST_ASSERT_EQUAL(typeid(dummy_function1).hash_code(), typeid(dummy_function2).hash_code()); } -#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS TEST_CASE("unsuccessful dynamic cast on reference throws exception", "[cxx]") { + test_utils_set_leak_level(LEAKS, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL); bool thrown = false; DerivedA derived_a; Base &base = derived_a; @@ -96,5 +124,8 @@ TEST_CASE("typeid on nullptr throws bad_typeid", "[cxx]") TEST_ASSERT(thrown); } -#endif // CONFIG_COMPILER_CXX_EXCEPTIONS -#endif // CONFIG_COMPILER_CXX_RTTI +extern "C" void app_main(void) +{ + printf("CXX RTTI TEST\n"); + unity_run_menu(); +} diff --git a/components/cxx/test_apps/rtti/pytest_cxx_rtti.py b/components/cxx/test_apps/rtti/pytest_cxx_rtti.py new file mode 100644 index 0000000000..1f8ea558a6 --- /dev/null +++ b/components/cxx/test_apps/rtti/pytest_cxx_rtti.py @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.esp32c3 +@pytest.mark.generic +def test_cxx_rtti(dut: Dut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('*') + dut.expect_unity_test_output() diff --git a/components/cxx/test_apps/rtti/sdkconfig.defaults b/components/cxx/test_apps/rtti/sdkconfig.defaults new file mode 100644 index 0000000000..091acbbe43 --- /dev/null +++ b/components/cxx/test_apps/rtti/sdkconfig.defaults @@ -0,0 +1,13 @@ +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 +CONFIG_COMPILER_CXX_RTTI=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_FREERTOS_HZ=1000 +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_ESP_TASK_WDT=n +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y +CONFIG_COMPILER_WARN_WRITE_STRINGS=y +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3000 diff --git a/tools/unit-test-app/configs/cxx_exceptions b/tools/unit-test-app/configs/cxx_exceptions deleted file mode 100644 index a66a4b8130..0000000000 --- a/tools/unit-test-app/configs/cxx_exceptions +++ /dev/null @@ -1,5 +0,0 @@ -# Only need to test this for one target (e.g. ESP32) per architecture -CONFIG_IDF_TARGET="esp32" -TEST_COMPONENTS=cxx -CONFIG_COMPILER_CXX_EXCEPTIONS=y -CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 diff --git a/tools/unit-test-app/configs/cxx_exceptions_c3 b/tools/unit-test-app/configs/cxx_exceptions_c3 deleted file mode 100644 index a86b483a1f..0000000000 --- a/tools/unit-test-app/configs/cxx_exceptions_c3 +++ /dev/null @@ -1,5 +0,0 @@ -# Only need to test this for one target (e.g. ESP32) per architecture -CONFIG_IDF_TARGET="esp32c3" -TEST_COMPONENTS=cxx -CONFIG_COMPILER_CXX_EXCEPTIONS=y -CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 diff --git a/tools/unit-test-app/configs/cxx_rtti_c3 b/tools/unit-test-app/configs/cxx_rtti_c3 deleted file mode 100644 index e9ec3be458..0000000000 --- a/tools/unit-test-app/configs/cxx_rtti_c3 +++ /dev/null @@ -1,6 +0,0 @@ -# Only need to test this for one target (e.g. ESP32) per architecture -CONFIG_IDF_TARGET="esp32c3" -TEST_COMPONENTS=cxx -CONFIG_COMPILER_CXX_EXCEPTIONS=y -CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024 -CONFIG_COMPILER_CXX_RTTI=y