From b522e9a0e151fc2938be794c0ba6c94f74887d3b Mon Sep 17 00:00:00 2001 From: dongheng Date: Mon, 18 Mar 2019 13:04:01 +0800 Subject: [PATCH] tools(unit-test-app): Modify for ESP8266 1. Remove unused or unsupported unit test 2. Add extra header file to pass compiling 3. Remove unsupported functions --- .../bootloader_support/test/component.mk | 4 - .../test/test_verify_image.c | 94 -- components/esp8266/include/esp_clk.h | 25 + components/esp8266/include/rom/ets_sys.h | 8 + components/esp8266/include/rom/uart.h | 127 ++ components/esp8266/include/soc/cpu.h | 40 + components/esp8266/ld/esp8266.rom.ld | 3 +- .../esp_http_server/test/CMakeLists.txt | 6 - components/esp_http_server/test/component.mk | 1 - .../esp_http_server/test/test_http_server.c | 169 --- components/freertos/port/esp8266/port.c | 8 + components/partition_table/test/component.mk | 5 - .../partition_table/test/test_partition.c | 97 -- components/protocomm/test/CMakeLists.txt | 7 - components/protocomm/test/component.mk | 2 - components/protocomm/test/test_protocomm.c | 1134 ----------------- .../components/unity/ref_clock.c | 170 --- .../components/unity/unity_platform.c | 2 +- tools/unit-test-app/configs/aes_no_hw | 3 - tools/unit-test-app/configs/app_update | 13 - tools/unit-test-app/configs/bt | 4 - tools/unit-test-app/configs/default | 1 - tools/unit-test-app/configs/libsodium | 3 - tools/unit-test-app/configs/psram | 3 - tools/unit-test-app/configs/psram_2 | 3 - tools/unit-test-app/configs/psram_8m | 4 - tools/unit-test-app/configs/release | 3 - tools/unit-test-app/configs/single_core | 4 - tools/unit-test-app/idf_ext.py | 279 ---- .../partition_table_unit_test_app.csv | 5 +- .../partition_table_unit_test_two_ota.csv | 11 - tools/unit-test-app/sdkconfig.defaults | 29 +- .../unit-test-app/tools/ConfigDependency.yml | 2 - .../unit-test-app/tools/CreateSectionTable.py | 163 --- .../unit-test-app/tools/ModuleDefinition.yml | 127 -- tools/unit-test-app/tools/TagDefinition.yml | 20 - tools/unit-test-app/tools/UnitTestParser.py | 318 ----- tools/unit-test-app/unit_test.py | 748 ----------- 38 files changed, 215 insertions(+), 3430 deletions(-) delete mode 100644 components/bootloader_support/test/component.mk delete mode 100644 components/bootloader_support/test/test_verify_image.c create mode 100644 components/esp8266/include/esp_clk.h create mode 100644 components/esp8266/include/rom/uart.h create mode 100644 components/esp8266/include/soc/cpu.h delete mode 100644 components/esp_http_server/test/CMakeLists.txt delete mode 100644 components/esp_http_server/test/component.mk delete mode 100644 components/esp_http_server/test/test_http_server.c delete mode 100644 components/partition_table/test/component.mk delete mode 100644 components/partition_table/test/test_partition.c delete mode 100644 components/protocomm/test/CMakeLists.txt delete mode 100644 components/protocomm/test/component.mk delete mode 100644 components/protocomm/test/test_protocomm.c delete mode 100644 tools/unit-test-app/components/unity/ref_clock.c delete mode 100644 tools/unit-test-app/configs/aes_no_hw delete mode 100644 tools/unit-test-app/configs/app_update delete mode 100644 tools/unit-test-app/configs/bt delete mode 100644 tools/unit-test-app/configs/default delete mode 100644 tools/unit-test-app/configs/libsodium delete mode 100644 tools/unit-test-app/configs/psram delete mode 100644 tools/unit-test-app/configs/psram_2 delete mode 100644 tools/unit-test-app/configs/psram_8m delete mode 100644 tools/unit-test-app/configs/release delete mode 100644 tools/unit-test-app/configs/single_core delete mode 100644 tools/unit-test-app/idf_ext.py delete mode 100644 tools/unit-test-app/partition_table_unit_test_two_ota.csv delete mode 100644 tools/unit-test-app/tools/ConfigDependency.yml delete mode 100644 tools/unit-test-app/tools/CreateSectionTable.py delete mode 100644 tools/unit-test-app/tools/ModuleDefinition.yml delete mode 100644 tools/unit-test-app/tools/TagDefinition.yml delete mode 100644 tools/unit-test-app/tools/UnitTestParser.py delete mode 100755 tools/unit-test-app/unit_test.py diff --git a/components/bootloader_support/test/component.mk b/components/bootloader_support/test/component.mk deleted file mode 100644 index 8c6eb513..00000000 --- a/components/bootloader_support/test/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -# -#Component Makefile -# -COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/bootloader_support/test/test_verify_image.c b/components/bootloader_support/test/test_verify_image.c deleted file mode 100644 index 7994667f..00000000 --- a/components/bootloader_support/test/test_verify_image.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Tests for bootloader_support esp_load(ESP_IMAGE_VERIFY, ...) - */ - -#include -#include -#include "string.h" -#include "rom/ets_sys.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "freertos/xtensa_api.h" -#include "unity.h" -#include "bootloader_common.h" -#include "esp_partition.h" -#include "esp_ota_ops.h" -#include "esp_image_format.h" - -TEST_CASE("Verify bootloader image in flash", "[bootloader_support]") -{ - const esp_partition_pos_t fake_bootloader_partition = { - .offset = ESP_BOOTLOADER_OFFSET, - .size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET, - }; - esp_image_metadata_t data = { 0 }; - TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_load(ESP_IMAGE_VERIFY, &fake_bootloader_partition, &data)); - TEST_ASSERT_NOT_EQUAL(0, data.image_len); - - uint32_t bootloader_length = 0; - TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_verify_bootloader(&bootloader_length)); - TEST_ASSERT_EQUAL(data.image_len, bootloader_length); -} - -TEST_CASE("Verify unit test app image", "[bootloader_support]") -{ - esp_image_metadata_t data = { 0 }; - const esp_partition_t *running = esp_ota_get_running_partition(); - TEST_ASSERT_NOT_EQUAL(NULL, running); - const esp_partition_pos_t running_pos = { - .offset = running->address, - .size = running->size, - }; - - TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_load(ESP_IMAGE_VERIFY, &running_pos, &data)); - TEST_ASSERT_NOT_EQUAL(0, data.image_len); - TEST_ASSERT_TRUE(data.image_len <= running->size); -} - -void check_label_search (int num_test, const char *list, const char *t_label, bool result) -{ - // gen_esp32part.py trims up to 16 characters - // and the string may not have a null terminal symbol. - // below is cutting as it does the generator. - char label[16 + 1] = {0}; - strncpy(label, t_label, sizeof(label) - 1); - - bool ret = bootloader_common_label_search(list, label); - if (ret != result) { - printf("%d) %s | %s \n", num_test, list, label); - } - TEST_ASSERT_MESSAGE(ret == result, "Test failed"); -} - -TEST_CASE("Test label_search", "[bootloader_support]") -{ - TEST_ASSERT_FALSE(bootloader_common_label_search(NULL, NULL)); - TEST_ASSERT_FALSE(bootloader_common_label_search("nvs", NULL)); - - check_label_search(1, "nvs", "nvs", true); - check_label_search(2, "nvs, ", "nvs", true); - check_label_search(3, "nvs1", "nvs", false); - check_label_search(3, "nvs1, ", "nvs", false); - check_label_search(4, "nvs1nvs1, phy", "nvs1", false); - check_label_search(5, "nvs1, nvs1, phy", "nvs1", true); - check_label_search(6, "nvs12, nvs12, phy", "nvs1", false); - check_label_search(7, "nvs12, nvs1, phy", "nvs1", true); - check_label_search(8, "nvs12, nvs3, phy, nvs1","nvs1", true); - check_label_search(9, "nvs1nvs1, phy, nvs", "nvs", true); - check_label_search(10, "nvs1nvs1, phy, nvs1", "nvs", false); - check_label_search(11, "nvs1, nvs, phy, nvs1", "nvs", true); - check_label_search(12, "nvs1, nvs2, phy, nvs","nvs", true); - check_label_search(13, "ota_data, backup_nvs", "nvs", false); - check_label_search(14, "nvs1, nvs2, ota, nvs", "vs1", false); - - check_label_search(20, "12345678901234, phy, nvs1", "12345678901234", true); - check_label_search(21, "123456789012345, phy, nvs1", "123456789012345", true); - check_label_search(22, "1234567890123456, phy, nvs1", "1234567890123456", true); - check_label_search(23, "12345678901234567, phy, nvs1", "12345678901234567", false); - check_label_search(24, "1234567890123456, phy, nvs1", "12345678901234567", true); - check_label_search(25, "phy, 1234567890123456, nvs1", "12345678901234567", true); - -} diff --git a/components/esp8266/include/esp_clk.h b/components/esp8266/include/esp_clk.h new file mode 100644 index 00000000..7298b090 --- /dev/null +++ b/components/esp8266/include/esp_clk.h @@ -0,0 +1,25 @@ +// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +/** + * @brief Return current CPU clock frequency + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return CPU clock frequency, in Hz + */ +int esp_clk_cpu_freq(void); diff --git a/components/esp8266/include/rom/ets_sys.h b/components/esp8266/include/rom/ets_sys.h index b5cbf0a5..2f37cf79 100644 --- a/components/esp8266/include/rom/ets_sys.h +++ b/components/esp8266/include/rom/ets_sys.h @@ -43,6 +43,14 @@ extern "C" { #define ETS_WDT_INUM 8 #define ETS_FRC_TIMER1_INUM 9 +typedef enum { + OK = 0, + FAIL, + PENDING, + BUSY, + CANCEL, +} STATUS; + extern char NMIIrqIsOn; extern uint32_t WDEV_INTEREST_EVENT; diff --git a/components/esp8266/include/rom/uart.h b/components/esp8266/include/rom/uart.h new file mode 100644 index 00000000..28eb365c --- /dev/null +++ b/components/esp8266/include/rom/uart.h @@ -0,0 +1,127 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _ROM_UART_H_ +#define _ROM_UART_H_ + +#include "esp_types.h" +#include "esp_attr.h" +#include "ets_sys.h" + +#include "esp8266/uart_struct.h" +#include "esp8266/uart_register.h" +#include "esp8266/pin_mux_register.h" +#include "esp8266/eagle_soc.h" +#include "esp8266/rom_functions.h" + +#include "driver/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \defgroup uart_apis, uart configuration and communication related apis + * @brief uart apis + */ + +/** @addtogroup uart_apis + * @{ + */ + +/** + * @brief Wait until uart tx full empty and the last char send ok. + * + * @param uart_no : 0 for UART0, 1 for UART1, 2 for UART2 + * + * The function defined in ROM code has a bug, so we define the correct version + * here for compatibility. + */ +static inline void IRAM_ATTR uart_tx_wait_idle(uint8_t uart_no) { + uint32_t tx_bytes; + uint32_t baudrate, byte_delay_us; + uart_dev_t *const UART[2] = {&uart0, &uart1}; + uart_dev_t *const uart = UART[uart_no]; + + baudrate = (UART_CLK_FREQ / (uart->clk_div.val & 0xFFFFF)); + byte_delay_us = (uint32_t)(10000000 / baudrate); + + do { + tx_bytes = uart->status.txfifo_cnt; + /* either tx count or state is non-zero */ + } while (tx_bytes); + + ets_delay_us(byte_delay_us); +} + +/** + * @brief Output a char to printf channel, wait until fifo not full. + * + * @param None + * + * @return OK. + */ +STATUS uart_tx_one_char(uint8_t TxChar); + +/** + * @brief Get an input char from message channel. + * Please do not call this function in SDK. + * + * @param uint8_t *pRxChar : the pointer to store the char. + * + * @return OK for successful. + * FAIL for failed. + */ +STATUS uart_rx_one_char(uint8_t *pRxChar); + +/** + * @brief Get an input string line from message channel. + * Please do not call this function in SDK. + * + * @param uint8_t *pString : the pointer to store the string. + * + * @param uint8_t MaxStrlen : the max string length, incude '\0'. + * + * @return OK. + */ +static inline STATUS UartRxString(uint8_t *pString, uint8_t MaxStrlen) +{ + int rx_bytes = 0; + + while(1) { + uint8_t data; + + while (uart_rx_one_char(&data) != OK); + + if (data == '\n' || data == '\r') + data = '\0'; + + pString[rx_bytes++] = data; + if (data == '\0') + return OK; + if (rx_bytes >= MaxStrlen) + return FAIL; + } + + return OK; +} + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _ROM_UART_H_ */ diff --git a/components/esp8266/include/soc/cpu.h b/components/esp8266/include/soc/cpu.h new file mode 100644 index 00000000..2249e06b --- /dev/null +++ b/components/esp8266/include/soc/cpu.h @@ -0,0 +1,40 @@ +// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef _SOC_CPU_H +#define _SOC_CPU_H + +#include +#include +#include +#include "xtensa/corebits.h" +#include "xtensa/config/core.h" + +/* C macros for xtensa special register read/write/exchange */ + +#define RSR(reg, curval) asm volatile ("rsr %0, " #reg : "=r" (curval)); +#define WSR(reg, newval) asm volatile ("wsr %0, " #reg : : "r" (newval)); +#define XSR(reg, swapval) asm volatile ("xsr %0, " #reg : "+r" (swapval)); + +/** @brief Read current stack pointer address + * + */ +static inline void *get_sp() +{ + void *sp; + asm volatile ("mov %0, sp;" : "=r" (sp)); + return sp; +} + +#endif diff --git a/components/esp8266/ld/esp8266.rom.ld b/components/esp8266/ld/esp8266.rom.ld index 49f6dd71..b51d44b7 100644 --- a/components/esp8266/ld/esp8266.rom.ld +++ b/components/esp8266/ld/esp8266.rom.ld @@ -57,4 +57,5 @@ PROVIDE ( gpio_input_get = 0x40004cf0 ); PROVIDE ( gpio_pin_wakeup_disable = 0x40004ed4 ); PROVIDE ( gpio_pin_wakeup_enable = 0x40004e90 ); -PROVIDE ( ets_io_vprintf = 0x40001f00 ); \ No newline at end of file +PROVIDE ( ets_io_vprintf = 0x40001f00 ); +PROVIDE ( uart_rx_one_char = 0x40003b8c ); \ No newline at end of file diff --git a/components/esp_http_server/test/CMakeLists.txt b/components/esp_http_server/test/CMakeLists.txt deleted file mode 100644 index 7587213b..00000000 --- a/components/esp_http_server/test/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") - -set(COMPONENT_REQUIRES unity esp_http_server) - -register_component() diff --git a/components/esp_http_server/test/component.mk b/components/esp_http_server/test/component.mk deleted file mode 100644 index ce464a21..00000000 --- a/components/esp_http_server/test/component.mk +++ /dev/null @@ -1 +0,0 @@ -COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/esp_http_server/test/test_http_server.c b/components/esp_http_server/test/test_http_server.c deleted file mode 100644 index e343c99c..00000000 --- a/components/esp_http_server/test/test_http_server.c +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -#include "unity.h" -#include "test_utils.h" - -int pre_start_mem, post_stop_mem, post_stop_min_mem; -bool basic_sanity = true; - -esp_err_t null_func(httpd_req_t *req) -{ - return ESP_OK; -} - -httpd_uri_t handler_limit_uri (char* path) -{ - httpd_uri_t uri = { - .uri = path, - .method = HTTP_GET, - .handler = null_func, - .user_ctx = NULL, - }; - return uri; -}; - -static inline unsigned num_digits(unsigned x) -{ - unsigned digits = 1; - while ((x = x/10) != 0) { - digits++; - } - return digits; -} - -#define HTTPD_TEST_MAX_URI_HANDLERS 8 - -void test_handler_limit(httpd_handle_t hd) -{ - int i; - char x[HTTPD_TEST_MAX_URI_HANDLERS+1][num_digits(HTTPD_TEST_MAX_URI_HANDLERS)+1]; - httpd_uri_t uris[HTTPD_TEST_MAX_URI_HANDLERS+1]; - - for (i = 0; i < HTTPD_TEST_MAX_URI_HANDLERS + 1; i++) { - sprintf(x[i],"%d",i); - uris[i] = handler_limit_uri(x[i]); - } - - /* Register multiple instances of the same handler for MAX URI Handlers */ - for (i = 0; i < HTTPD_TEST_MAX_URI_HANDLERS; i++) { - TEST_ASSERT(httpd_register_uri_handler(hd, &uris[i]) == ESP_OK); - } - - /* Register the MAX URI + 1 Handlers should fail */ - TEST_ASSERT(httpd_register_uri_handler(hd, &uris[HTTPD_TEST_MAX_URI_HANDLERS]) != ESP_OK); - - /* Unregister the one of the Handler should pass */ - TEST_ASSERT(httpd_unregister_uri_handler(hd, uris[0].uri, uris[0].method) == ESP_OK); - - /* Unregister non added Handler should fail */ - TEST_ASSERT(httpd_unregister_uri_handler(hd, uris[0].uri, uris[0].method) != ESP_OK); - - /* Register the MAX URI Handler should pass */ - TEST_ASSERT(httpd_register_uri_handler(hd, &uris[0]) == ESP_OK); - - /* Reregister same instance of handler should fail */ - TEST_ASSERT(httpd_register_uri_handler(hd, &uris[0]) != ESP_OK); - - /* Register the MAX URI + 1 Handlers should fail */ - TEST_ASSERT(httpd_register_uri_handler(hd, &uris[HTTPD_TEST_MAX_URI_HANDLERS]) != ESP_OK); - - /* Unregister the same handler for MAX URI Handlers */ - for (i = 0; i < HTTPD_TEST_MAX_URI_HANDLERS; i++) { - TEST_ASSERT(httpd_unregister_uri_handler(hd, uris[i].uri, uris[i].method) == ESP_OK); - } - basic_sanity = false; -} - -/********************* Test Handler Limit End *******************/ - -httpd_handle_t test_httpd_start(uint16_t id) -{ - httpd_handle_t hd; - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.max_uri_handlers = HTTPD_TEST_MAX_URI_HANDLERS; - config.server_port += id; - config.ctrl_port += id; - TEST_ASSERT(httpd_start(&hd, &config) == ESP_OK) - return hd; -} - -#define SERVER_INSTANCES 2 - -/* Currently this only tests for the number of tasks. - * Heap leakage is not tested as LWIP allocates memory - * which may not be freed immedietly causing erroneous - * evaluation. Another test to implement would be the - * monitoring of open sockets, but LWIP presently provides - * no such API for getting the number of open sockets. - */ -TEST_CASE("Leak Test", "[HTTP SERVER]") -{ - httpd_handle_t hd[SERVER_INSTANCES]; - unsigned task_count; - bool res = true; - - test_case_uses_tcpip(); - - task_count = uxTaskGetNumberOfTasks(); - printf("Initial task count: %d\n", task_count); - - pre_start_mem = esp_get_free_heap_size(); - - for (int i = 0; i < SERVER_INSTANCES; i++) { - hd[i] = test_httpd_start(i); - vTaskDelay(10); - unsigned num_tasks = uxTaskGetNumberOfTasks(); - task_count++; - if (num_tasks != task_count) { - printf("Incorrect task count (starting): %d expected %d\n", - num_tasks, task_count); - res = false; - } - } - - for (int i = 0; i < SERVER_INSTANCES; i++) { - if (httpd_stop(hd[i]) != ESP_OK) { - printf("Failed to stop httpd task %d\n", i); - res = false; - } - vTaskDelay(10); - unsigned num_tasks = uxTaskGetNumberOfTasks(); - task_count--; - if (num_tasks != task_count) { - printf("Incorrect task count (stopping): %d expected %d\n", - num_tasks, task_count); - res = false; - } - } - post_stop_mem = esp_get_free_heap_size(); - TEST_ASSERT(res == true); -} - -TEST_CASE("Basic Functionality Tests", "[HTTP SERVER]") -{ - httpd_handle_t hd; - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - - test_case_uses_tcpip(); - - TEST_ASSERT(httpd_start(&hd, &config) == ESP_OK); - test_handler_limit(hd); - TEST_ASSERT(httpd_stop(hd) == ESP_OK); -} diff --git a/components/freertos/port/esp8266/port.c b/components/freertos/port/esp8266/port.c index cae53e84..4066345d 100644 --- a/components/freertos/port/esp8266/port.c +++ b/components/freertos/port/esp8266/port.c @@ -163,6 +163,14 @@ void TASK_SW_ATTR xPortSysTickHandle(void) } } +/** + * @brief Return current CPU clock frequency + */ +int esp_clk_cpu_freq(void) +{ + return _xt_tick_divisor * XT_TICK_PER_SEC; +} + /* * See header file for description. */ diff --git a/components/partition_table/test/component.mk b/components/partition_table/test/component.mk deleted file mode 100644 index 5dd172bd..00000000 --- a/components/partition_table/test/component.mk +++ /dev/null @@ -1,5 +0,0 @@ -# -#Component Makefile -# - -COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/partition_table/test/test_partition.c b/components/partition_table/test/test_partition.c deleted file mode 100644 index 5267b5ba..00000000 --- a/components/partition_table/test/test_partition.c +++ /dev/null @@ -1,97 +0,0 @@ -#include -#include -#include "unity.h" -#include "test_utils.h" -#include "esp_partition.h" - - -TEST_CASE("Can read partition table", "[partition]") -{ - - const esp_partition_t *p = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); - TEST_ASSERT_NOT_NULL(p); - TEST_ASSERT_EQUAL(0x10000, p->address); - TEST_ASSERT_EQUAL(ESP_PARTITION_SUBTYPE_APP_FACTORY, p->subtype); - - esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL); - TEST_ASSERT_NOT_NULL(it); - int count = 0; - const esp_partition_t* prev = NULL; - for (; it != NULL; it = esp_partition_next(it)) { - const esp_partition_t *p = esp_partition_get(it); - TEST_ASSERT_NOT_NULL(p); - if (prev) { - TEST_ASSERT_TRUE_MESSAGE(prev->address < p->address, "incorrect partition order"); - } - prev = p; - ++count; - } - esp_partition_iterator_release(it); - TEST_ASSERT_EQUAL(4, count); -} - -TEST_CASE("Can write, read, mmap partition", "[partition][ignore]") -{ - const esp_partition_t *p = get_test_data_partition(); - printf("Using partition %s at 0x%x, size 0x%x\n", p->label, p->address, p->size); - TEST_ASSERT_NOT_NULL(p); - const size_t max_size = 2 * SPI_FLASH_SEC_SIZE; - uint8_t *data = (uint8_t *) malloc(max_size); - TEST_ASSERT_NOT_NULL(data); - - TEST_ASSERT_EQUAL(ESP_OK, esp_partition_erase_range(p, 0, p->size)); - - srand(0); - size_t block_size; - for (size_t offset = 0; offset < p->size; offset += block_size) { - block_size = ((rand() + 4) % max_size) & (~0x3); - size_t left = p->size - offset; - if (block_size > left) { - block_size = left; - } - for (size_t i = 0; i < block_size / 4; ++i) { - ((uint32_t *) (data))[i] = rand(); - } - TEST_ASSERT_EQUAL(ESP_OK, esp_partition_write(p, offset, data, block_size)); - } - - srand(0); - for (size_t offset = 0; offset < p->size; offset += block_size) { - block_size = ((rand() + 4) % max_size) & (~0x3); - size_t left = p->size - offset; - if (block_size > left) { - block_size = left; - } - TEST_ASSERT_EQUAL(ESP_OK, esp_partition_read(p, offset, data, block_size)); - for (size_t i = 0; i < block_size / 4; ++i) { - TEST_ASSERT_EQUAL(rand(), ((uint32_t *) data)[i]); - } - } - - free(data); - - const uint32_t *mmap_data; - spi_flash_mmap_handle_t mmap_handle; - size_t begin = 3000; - size_t size = 64000; //chosen so size is smaller than 64K but the mmap straddles 2 MMU blocks - TEST_ASSERT_EQUAL(ESP_OK, esp_partition_mmap(p, begin, size, SPI_FLASH_MMAP_DATA, - (const void **)&mmap_data, &mmap_handle)); - srand(0); - for (size_t offset = 0; offset < p->size; offset += block_size) { - block_size = ((rand() + 4) % max_size) & (~0x3); - size_t left = p->size - offset; - if (block_size > left) { - block_size = left; - } - for (size_t i = 0; i < block_size / 4; ++i) { - size_t pos = offset + i * 4; - uint32_t expected = rand(); - if (pos < begin || pos >= (begin + size)) { - continue; - } - TEST_ASSERT_EQUAL(expected, mmap_data[(pos - begin) / 4]); - } - } - - spi_flash_munmap(mmap_handle); -} diff --git a/components/protocomm/test/CMakeLists.txt b/components/protocomm/test/CMakeLists.txt deleted file mode 100644 index f25cb01a..00000000 --- a/components/protocomm/test/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(COMPONENT_SRCDIRS ".") -set(COMPONENT_ADD_INCLUDEDIRS ".") -set(COMPONENT_PRIV_INCLUDEDIRS "../proto-c/") - -set(COMPONENT_REQUIRES unity mbedtls protocomm protobuf-c) - -register_component() diff --git a/components/protocomm/test/component.mk b/components/protocomm/test/component.mk deleted file mode 100644 index 7a27118a..00000000 --- a/components/protocomm/test/component.mk +++ /dev/null @@ -1,2 +0,0 @@ -COMPONENT_PRIV_INCLUDEDIRS := ../proto-c/ -COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive diff --git a/components/protocomm/test/test_protocomm.c b/components/protocomm/test/test_protocomm.c deleted file mode 100644 index d9a3a71a..00000000 --- a/components/protocomm/test/test_protocomm.c +++ /dev/null @@ -1,1134 +0,0 @@ -// Copyright 2018 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "session.pb-c.h" - -#ifdef CONFIG_HEAP_TRACING - #include - #define NUM_RECORDS 100 - static heap_trace_record_t trace_record[NUM_RECORDS]; // This buffer must be in internal RAM -#endif - -#define PUBLIC_KEY_LEN 32 -#define SZ_RANDOM 16 - -typedef struct { - uint32_t id; - uint8_t sec_ver; - uint8_t weak; - const protocomm_security_pop_t *pop; - uint8_t device_pubkey[PUBLIC_KEY_LEN]; - uint8_t client_pubkey[PUBLIC_KEY_LEN]; - uint8_t sym_key[PUBLIC_KEY_LEN]; - uint8_t rand[SZ_RANDOM]; - - /* mbedtls context data for Curve25519 */ - mbedtls_ecdh_context ctx_client; - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - - /* mbedtls context data for AES */ - mbedtls_aes_context ctx_aes; - unsigned char stb[16]; - size_t nc_off; -} session_t; - -static const char *TAG = "protocomm_test"; - -static protocomm_t *test_pc = NULL; -static const protocomm_security_t *test_sec = NULL; -static uint32_t test_priv_data = 1234; - -static void flip_endian(uint8_t *data, size_t len) -{ - uint8_t swp_buf; - for (int i = 0; i < len/2; i++) { - swp_buf = data[i]; - data[i] = data[len - i - 1]; - data[len - i - 1] = swp_buf; - } -} - -static void hexdump(const char *msg, uint8_t *buf, int len) -{ - ESP_LOGI(TAG, "%s:", msg); - ESP_LOG_BUFFER_HEX(TAG, buf, len); -} - -static esp_err_t prepare_command0(session_t *session, SessionData *req) -{ - Sec1Payload *in = (Sec1Payload *) malloc(sizeof(Sec1Payload)); - if (in == NULL) { - ESP_LOGE(TAG, "Error allocating memory for request"); - return ESP_ERR_NO_MEM; - } - - SessionCmd0 *in_req = (SessionCmd0 *) malloc(sizeof(SessionCmd0)); - if (in_req == NULL) { - ESP_LOGE(TAG, "Error allocating memory for request"); - free(in); - return ESP_ERR_NO_MEM; - } - - sec1_payload__init(in); - session_cmd0__init(in_req); - - in_req->client_pubkey.data = session->client_pubkey; - in_req->client_pubkey.len = PUBLIC_KEY_LEN; - - in->msg = SEC1_MSG_TYPE__Session_Command0; - in->payload_case = SEC1_PAYLOAD__PAYLOAD_SC0; - in->sc0 = in_req; - - req->proto_case = SESSION_DATA__PROTO_SEC1; - req->sec_ver = protocomm_security1.ver; - req->sec1 = in; - - return ESP_OK; -} - -static void cleanup_command0(SessionData *req) -{ - free(req->sec1->sc0); - free(req->sec1); -} - -static esp_err_t verify_response0(session_t *session, SessionData *resp) -{ - if ((resp->proto_case != SESSION_DATA__PROTO_SEC1) || - (resp->sec1->msg != SEC1_MSG_TYPE__Session_Response0)) { - ESP_LOGE(TAG, "Invalid response type"); - return ESP_ERR_INVALID_ARG; - } - - int ret; - Sec1Payload *in = (Sec1Payload *) resp->sec1; - - if (in->sr0->device_pubkey.len != PUBLIC_KEY_LEN) { - ESP_LOGE(TAG, "Device public key length as not as expected"); - return ESP_FAIL; - } - - if (in->sr0->device_random.len != SZ_RANDOM) { - ESP_LOGE(TAG, "Device random data length is not as expected"); - return ESP_FAIL; - } - - uint8_t *cli_pubkey = session->client_pubkey; - uint8_t *dev_pubkey = session->device_pubkey; - memcpy(session->device_pubkey, in->sr0->device_pubkey.data, in->sr0->device_pubkey.len); - - hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN); - hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN); - - ret = mbedtls_mpi_lset(&session->ctx_client.Qp.Z, 1); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_mpi_lset with error code : %d", ret); - return ESP_FAIL; - } - - flip_endian(session->device_pubkey, PUBLIC_KEY_LEN); - ret = mbedtls_mpi_read_binary(&session->ctx_client.Qp.X, dev_pubkey, PUBLIC_KEY_LEN); - flip_endian(session->device_pubkey, PUBLIC_KEY_LEN); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : %d", ret); - return ESP_FAIL; - } - - ret = mbedtls_ecdh_compute_shared(&session->ctx_client.grp, - &session->ctx_client.z, - &session->ctx_client.Qp, - &session->ctx_client.d, - mbedtls_ctr_drbg_random, - &session->ctr_drbg); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_ecdh_compute_shared with error code : %d", ret); - return ESP_FAIL; - } - - ret = mbedtls_mpi_write_binary(&session->ctx_client.z, session->sym_key, PUBLIC_KEY_LEN); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : %d", ret); - return ESP_FAIL; - } - flip_endian(session->sym_key, PUBLIC_KEY_LEN); - - const protocomm_security_pop_t *pop = session->pop; - if (pop != NULL && pop->data != NULL && pop->len != 0) { - ESP_LOGD(TAG, "Adding proof of possession"); - uint8_t sha_out[PUBLIC_KEY_LEN]; - - ret = mbedtls_sha256_ret((const unsigned char *) pop->data, pop->len, sha_out, 0); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_sha256_ret with error code : %d", ret); - return ESP_FAIL; - } - - for (int i = 0; i < PUBLIC_KEY_LEN; i++) { - session->sym_key[i] ^= sha_out[i]; - } - } - - hexdump("Shared key", session->sym_key, PUBLIC_KEY_LEN); - - memcpy(session->rand, in->sr0->device_random.data, in->sr0->device_random.len); - hexdump("Dev random", session->rand, sizeof(session->rand)); - return ESP_OK; -} - -static esp_err_t prepare_command1(session_t *session, SessionData *req) -{ - int ret; - uint8_t *outbuf = (uint8_t *) malloc(PUBLIC_KEY_LEN); - if (!outbuf) { - ESP_LOGE(TAG, "Error allocating ciphertext buffer"); - return ESP_ERR_NO_MEM; - } - - /* Initialise crypto context */ - mbedtls_aes_init(&session->ctx_aes); - memset(session->stb, 0, sizeof(session->stb)); - session->nc_off = 0; - - ret = mbedtls_aes_setkey_enc(&session->ctx_aes, session->sym_key, - sizeof(session->sym_key)*8); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_aes_setkey_enc with erro code : %d", ret); - free(outbuf); - return ESP_FAIL; - } - - ret = mbedtls_aes_crypt_ctr(&session->ctx_aes, PUBLIC_KEY_LEN, - &session->nc_off, session->rand, - session->stb, session->device_pubkey, outbuf); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with erro code : %d", ret); - free(outbuf); - return ESP_FAIL; - } - - Sec1Payload *out = (Sec1Payload *) malloc(sizeof(Sec1Payload)); - if (!out) { - ESP_LOGE(TAG, "Error allocating out buffer"); - free(outbuf); - return ESP_ERR_NO_MEM; - } - sec1_payload__init(out); - - SessionCmd1 *out_req = (SessionCmd1 *) malloc(sizeof(SessionCmd1)); - if (!out_req) { - ESP_LOGE(TAG, "Error allocating out_req buffer"); - free(outbuf); - free(out); - return ESP_ERR_NO_MEM; - } - session_cmd1__init(out_req); - - out_req->client_verify_data.data = outbuf; - out_req->client_verify_data.len = PUBLIC_KEY_LEN; - hexdump("Client verify data", outbuf, PUBLIC_KEY_LEN); - - out->msg = SEC1_MSG_TYPE__Session_Command1; - out->payload_case = SEC1_PAYLOAD__PAYLOAD_SC1; - out->sc1 = out_req; - - req->proto_case = SESSION_DATA__PROTO_SEC1; - req->sec_ver = protocomm_security1.ver; - req->sec1 = out; - - return ESP_OK; -} - -static void cleanup_command1(SessionData *req) -{ - free(req->sec1->sc1->client_verify_data.data); - free(req->sec1->sc1); - free(req->sec1); -} - -static esp_err_t verify_response1(session_t *session, SessionData *resp) -{ - uint8_t *cli_pubkey = session->client_pubkey; - uint8_t *dev_pubkey = session->device_pubkey; - - hexdump("Device pubkey", dev_pubkey, PUBLIC_KEY_LEN); - hexdump("Client pubkey", cli_pubkey, PUBLIC_KEY_LEN); - - if ((resp->proto_case != SESSION_DATA__PROTO_SEC1) || - (resp->sec1->msg != SEC1_MSG_TYPE__Session_Response1)) { - ESP_LOGE(TAG, "Invalid response type"); - return ESP_ERR_INVALID_ARG; - } - - uint8_t check_buf[PUBLIC_KEY_LEN]; - Sec1Payload *in = (Sec1Payload *) resp->sec1; - - int ret = mbedtls_aes_crypt_ctr(&session->ctx_aes, PUBLIC_KEY_LEN, - &session->nc_off, session->rand, session->stb, - in->sr1->device_verify_data.data, check_buf); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_aes_crypt_ctr with erro code : %d", ret); - return ESP_FAIL; - } - hexdump("Dec Device verifier", check_buf, sizeof(check_buf)); - - if (memcmp(check_buf, session->client_pubkey, sizeof(session->client_pubkey)) != 0) { - ESP_LOGE(TAG, "Key mismatch. Close connection"); - return ESP_FAIL; - } - - return ESP_OK; -} - -static esp_err_t test_new_session(session_t *session) -{ - if (session->sec_ver == 0) { - return ESP_OK; - } - - if (!test_sec || !test_sec->new_transport_session) { - return ESP_ERR_INVALID_STATE; - } - - uint32_t session_id = session->id; - if (test_sec->new_transport_session(session_id) != ESP_OK) { - ESP_LOGE(TAG, "Failed to launch new transport session"); - return ESP_FAIL; - } - return ESP_OK; -} - -static esp_err_t test_sec_endpoint(session_t *session) -{ - if (session->sec_ver == 0) { - return ESP_OK; - } - - uint32_t session_id = session->id; - - int ret = ESP_FAIL; - SessionData req; - SessionData *resp; - ssize_t inlen = 0; - uint8_t *inbuf = NULL; - ssize_t outlen = 0; - uint8_t *outbuf = NULL; - - mbedtls_ecdh_init(&session->ctx_client); - mbedtls_ctr_drbg_init(&session->ctr_drbg); - - mbedtls_entropy_init(&session->entropy); - ret = mbedtls_ctr_drbg_seed(&session->ctr_drbg, mbedtls_entropy_func, - &session->entropy, NULL, 0); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_ctr_drbg_seed with error code : %d", ret); - goto abort_test_sec_endpoint; - } - - ret = mbedtls_ecp_group_load(&session->ctx_client.grp, MBEDTLS_ECP_DP_CURVE25519); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_ecp_group_load with error code : %d", ret); - goto abort_test_sec_endpoint; - } - - ret = mbedtls_ecdh_gen_public(&session->ctx_client.grp, - &session->ctx_client.d, - &session->ctx_client.Q, - mbedtls_ctr_drbg_random, - &session->ctr_drbg); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_ecdh_gen_public with error code : %d", ret); - goto abort_test_sec_endpoint; - } - - if (session->weak) { - /* Read zero client public key */ - ret = mbedtls_mpi_read_binary(&session->ctx_client.Q.X, - session->client_pubkey, - PUBLIC_KEY_LEN); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_mpi_read_binary with error code : %d", ret); - goto abort_test_sec_endpoint; - } - } - ret = mbedtls_mpi_write_binary(&session->ctx_client.Q.X, - session->client_pubkey, - PUBLIC_KEY_LEN); - if (ret != 0) { - ESP_LOGE(TAG, "Failed at mbedtls_mpi_write_binary with error code : %d", ret); - goto abort_test_sec_endpoint; - } - flip_endian(session->client_pubkey, PUBLIC_KEY_LEN); - - /*********** Transaction0 = SessionCmd0 + SessionResp0 ****************/ - session_data__init(&req); - if (prepare_command0(session, &req) != ESP_OK) { - ESP_LOGE(TAG, "Failed in prepare_command0"); - goto abort_test_sec_endpoint; - } - - inlen = session_data__get_packed_size(&req); - inbuf = (uint8_t *) malloc(inlen); - if (!inbuf) { - ESP_LOGE(TAG, "Failed to allocate inbuf"); - goto abort_test_sec_endpoint; - } - - session_data__pack(&req, inbuf); - cleanup_command0(&req); - - outlen = 0; - outbuf = NULL; - ret = protocomm_req_handle(test_pc, "test-sec", session_id, - inbuf, inlen, &outbuf, &outlen); - - free(inbuf); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "test-sec handler failed"); - free(outbuf); - goto abort_test_sec_endpoint; - } - - resp = session_data__unpack(NULL, outlen, outbuf); - free(outbuf); - if (!resp) { - ESP_LOGE(TAG, "Unable to unpack SessionResp0"); - goto abort_test_sec_endpoint; - } - - if (verify_response0(session, resp) != ESP_OK) { - ESP_LOGE(TAG, "Invalid response 0"); - session_data__free_unpacked(resp, NULL); - goto abort_test_sec_endpoint; - } - - session_data__free_unpacked(resp, NULL); - - /*********** Transaction1 = SessionCmd1 + SessionResp1 ****************/ - session_data__init(&req); - if (prepare_command1(session, &req) != ESP_OK) { - ESP_LOGE(TAG, "Failed in prepare_command1"); - goto abort_test_sec_endpoint; - } - - inlen = session_data__get_packed_size(&req); - inbuf = (uint8_t *) malloc(inlen); - if (!inbuf) { - ESP_LOGE(TAG, "Failed to allocate inbuf"); - goto abort_test_sec_endpoint; - } - - session_data__pack(&req, inbuf); - cleanup_command1(&req); - - outlen = 0; - outbuf = NULL; - ret = protocomm_req_handle(test_pc, "test-sec", session_id, - inbuf, inlen, &outbuf, &outlen); - - free(inbuf); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "test-sec handler failed"); - free(outbuf); - goto abort_test_sec_endpoint; - } - - resp = session_data__unpack(NULL, outlen, outbuf); - free(outbuf); - if (!resp) { - ESP_LOGE(TAG, "Unable to unpack SessionResp0"); - goto abort_test_sec_endpoint; - } - - if (verify_response1(session, resp) != ESP_OK) { - ESP_LOGE(TAG, "Invalid response 1"); - session_data__free_unpacked(resp, NULL); - goto abort_test_sec_endpoint; - } - - session_data__free_unpacked(resp, NULL); - mbedtls_ecdh_free(&session->ctx_client); - mbedtls_ctr_drbg_free(&session->ctr_drbg); - mbedtls_entropy_free(&session->entropy); - return ESP_OK; - -abort_test_sec_endpoint: - mbedtls_ecdh_free(&session->ctx_client); - mbedtls_ctr_drbg_free(&session->ctr_drbg); - mbedtls_entropy_free(&session->entropy); - return ESP_FAIL; -} - -#define TEST_VER_STR "" - -static esp_err_t test_ver_endpoint(session_t *session) -{ - ssize_t ver_data_len = 0; - uint8_t *ver_data = NULL; - - esp_err_t ret = protocomm_req_handle(test_pc, "test-ver", session->id, - NULL, 0, &ver_data, &ver_data_len); - - if (ret != ESP_OK) { - ESP_LOGE(TAG, "test-ver handler failed"); - return ESP_FAIL; - } - - if (ver_data_len != strlen(TEST_VER_STR) || memcmp(TEST_VER_STR, ver_data, ver_data_len)) { - ESP_LOGE(TAG, "incorrect response data from test-ver"); - free(ver_data); - return ESP_FAIL; - } - free(ver_data); - return ESP_OK; -} - -static esp_err_t test_req_endpoint(session_t *session) -{ - uint32_t session_id = session->id; - - uint8_t rand_test_data[512], enc_test_data[512]; - getrandom(rand_test_data, sizeof(rand_test_data), 0); - - if (session->sec_ver == 0) { - memcpy(enc_test_data, rand_test_data, sizeof(rand_test_data)); - } - else if (session->sec_ver == 1) { - mbedtls_aes_crypt_ctr(&session->ctx_aes, sizeof(rand_test_data), &session->nc_off, - session->rand, session->stb, rand_test_data, enc_test_data); - } - - ssize_t verify_data_len = 0; - uint8_t *enc_verify_data = NULL; - - esp_err_t ret = protocomm_req_handle(test_pc, "test-ep", session_id, - enc_test_data, sizeof(enc_test_data), - &enc_verify_data, &verify_data_len); - - if (ret != ESP_OK || !verify_data_len) { - ESP_LOGE(TAG, "test-ep handler failed"); - return ESP_FAIL; - } - - uint8_t *verify_data = malloc(verify_data_len); - if (!verify_data) { - ESP_LOGE(TAG, "error allocating memory for decrypted data"); - free(enc_verify_data); - return ESP_FAIL; - } - - if (session->sec_ver == 0) { - memcpy(verify_data, enc_verify_data, verify_data_len); - } - else if (session->sec_ver == 1) { - mbedtls_aes_crypt_ctr(&session->ctx_aes, verify_data_len, &session->nc_off, - session->rand, session->stb, enc_verify_data, verify_data); - } - free(enc_verify_data); - - hexdump("Sent data", rand_test_data, sizeof(rand_test_data)); - hexdump("Recv data", verify_data, verify_data_len); - - ESP_LOGI(TAG, "verify data len : %d", verify_data_len); - ESP_LOGI(TAG, "expected data len : %d", sizeof(rand_test_data)); - - if (verify_data_len != sizeof(rand_test_data)) { - ESP_LOGE(TAG, "incorrect response length from test-ep"); - free(verify_data); - return ESP_FAIL; - } - if (memcmp(rand_test_data, verify_data, verify_data_len)) { - ESP_LOGE(TAG, "incorrect response data from test-ep"); - free(verify_data); - return ESP_FAIL; - } - free(verify_data); - return ESP_OK; -} - -esp_err_t test_req_handler (uint32_t session_id, - const uint8_t *inbuf, ssize_t inlen, - uint8_t **outbuf, ssize_t *outlen, - void *priv_data) -{ - *outbuf = malloc(inlen); - if (*outbuf) { - *outlen = inlen; - memcpy(*outbuf, inbuf, inlen); - } else { - ESP_LOGE(TAG, "Error allocating response outbuf"); - *outbuf = NULL; - *outlen = 0; - } - - uint32_t *priv = (uint32_t *) priv_data; - if ((&test_priv_data != priv) || (test_priv_data != *priv)) { - ESP_LOGE(TAG, "Handler private data doesn't match"); - return ESP_FAIL; - } - return ESP_OK; -} - -static esp_err_t start_test_service(uint8_t sec_ver, const protocomm_security_pop_t *pop) -{ - test_pc = protocomm_new(); - if (test_pc == NULL) { - ESP_LOGE(TAG, "Failed to create new protocomm instance"); - return ESP_FAIL; - } - - if (sec_ver == 0) { - if (protocomm_set_security(test_pc, "test-sec", &protocomm_security0, NULL) != ESP_OK) { - ESP_LOGE(TAG, "Failed to set Security0"); - return ESP_FAIL; - } - test_sec = &protocomm_security0; - } else if (sec_ver == 1) { - if (protocomm_set_security(test_pc, "test-sec", &protocomm_security1, pop) != ESP_OK) { - ESP_LOGE(TAG, "Failed to set Security1"); - return ESP_FAIL; - } - test_sec = &protocomm_security1; - } - - if (protocomm_set_version(test_pc, "test-ver", TEST_VER_STR) != ESP_OK) { - ESP_LOGE(TAG, "Failed to set version"); - return ESP_FAIL; - } - - if (protocomm_add_endpoint(test_pc, "test-ep", - test_req_handler, - (void *) &test_priv_data) != ESP_OK) { - ESP_LOGE(TAG, "Failed to set test-ep endpoint handler"); - return ESP_FAIL; - } - return ESP_OK; -} - -static void stop_test_service(void) -{ - test_sec = NULL; - protocomm_delete(test_pc); - test_pc = NULL; -} - -static esp_err_t test_security1_no_encryption (void) -{ - ESP_LOGI(TAG, "Starting Security 1 no encryption test"); - - const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { - .data = (const uint8_t *)pop_data, - .len = strlen(pop_data) - }; - - session_t *session = calloc(1, sizeof(session_t)); - if (session == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session->id = 1; - session->sec_ver = 1; - session->pop = &pop; - - // Start protocomm service - if (start_test_service(1, &pop) != ESP_OK) { - ESP_LOGE(TAG, "Error starting test"); - free(session); - return ESP_ERR_INVALID_STATE; - } - - // Intialise protocomm session with zero public keys - if (test_new_session(session) != ESP_OK) { - ESP_LOGE(TAG, "Error creating new session"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - // Perform 25519 security handshake to set public keys - if (test_sec_endpoint(session) != ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - // Force endpoint with un-encrypted data - session->sec_ver = 0; - - // Send unencrypted request data to echo endpoint. - // Response would be encrypted causing echoed back - // data to not match that which was sent, hence failing. - if (test_req_endpoint(session) == ESP_OK) { - ESP_LOGE(TAG, "Error testing request endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - stop_test_service(); - free(session); - ESP_LOGI(TAG, "Protocomm test successful"); - return ESP_OK; -} - -static esp_err_t test_security1_session_overflow (void) -{ - ESP_LOGI(TAG, "Starting Security 1 session overflow test"); - - const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { - .data = (const uint8_t *)pop_data, - .len = strlen(pop_data) - }; - - session_t *session1 = calloc(1, sizeof(session_t)); - if (session1 == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session1->id = 2; - session1->sec_ver = 1; - session1->pop = &pop; - - session_t *session2 = calloc(1, sizeof(session_t)); - if (session2 == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - free(session1); - return ESP_ERR_NO_MEM; - } - - session2->id = 3; - session2->sec_ver = 1; - session2->pop = NULL; - - // Start protocomm service - if (start_test_service(1, &pop) != ESP_OK) { - ESP_LOGE(TAG, "Error starting test"); - free(session1); - free(session2); - return ESP_FAIL; - } - - // Intialise protocomm session with zero public keys - if (test_new_session(session1) != ESP_OK) { - ESP_LOGE(TAG, "Error creating new session"); - stop_test_service(); - free(session1); - free(session2); - return ESP_FAIL; - } - - // Perform 25519 security handshake to set public keys - if (test_sec_endpoint(session1) != ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - free(session1); - free(session2); - return ESP_FAIL; - } - - // Try to perform security handshake again with different - // session ID without registering new session, hence failing - if (test_sec_endpoint(session2) == ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - free(session1); - free(session2); - return ESP_FAIL; - } - - stop_test_service(); - free(session1); - free(session2); - - ESP_LOGI(TAG, "Protocomm test successful"); - return ESP_OK; -} - -static esp_err_t test_security1_wrong_pop (void) -{ - ESP_LOGI(TAG, "Starting Security 1 wrong auth test"); - - const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { - .data = (const uint8_t *)pop_data, - .len = strlen(pop_data) - }; - - session_t *session = calloc(1, sizeof(session_t)); - if (session == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session->id = 4; - session->sec_ver = 1; - session->pop = &pop; - - // Start protocomm service - if (start_test_service(1, &pop) != ESP_OK) { - ESP_LOGE(TAG, "Error starting test"); - free(session); - return ESP_FAIL; - } - - // Intialise protocomm session with zero public keys - if (test_new_session(session) != ESP_OK) { - ESP_LOGE(TAG, "Error creating new session"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - const char *wrong_pop_data = "wrong pop"; - protocomm_security_pop_t wrong_pop = { - .data = (const uint8_t *)wrong_pop_data, - .len = strlen(wrong_pop_data) - }; - - // Force wrong pop during authentication - session->pop = &wrong_pop; - - // Perform 25519 security handshake with - // wrong pop, hence failing - if (test_sec_endpoint(session) == ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - stop_test_service(); - free(session); - - ESP_LOGI(TAG, "Protocomm test successful"); - return ESP_OK; -} - -static esp_err_t test_security1_insecure_client (void) -{ - ESP_LOGI(TAG, "Starting Security 1 insecure client test"); - - const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { - .data = (const uint8_t *)pop_data, - .len = strlen(pop_data) - }; - - session_t *session = calloc(1, sizeof(session_t)); - if (session == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session->id = 5; - session->sec_ver = 1; - session->pop = &pop; - - // Start protocomm service - if (start_test_service(1, &pop) != ESP_OK) { - ESP_LOGE(TAG, "Error starting test"); - free(session); - return ESP_FAIL; - } - - // Perform 25519 security handshake without - // initialising session, hence failing - if (test_sec_endpoint(session) == ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - // Communicating with request endpoint without - // initialising session, hence failing - if (test_req_endpoint(session) == ESP_OK) { - ESP_LOGE(TAG, "Error testing request endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - stop_test_service(); - free(session); - - ESP_LOGI(TAG, "Protocomm test successful"); - return ESP_OK; -} - -static esp_err_t test_security1_weak_session (void) -{ - ESP_LOGI(TAG, "Starting Security 1 weak session test"); - - const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { - .data = (const uint8_t *)pop_data, - .len = strlen(pop_data) - }; - - session_t *session = calloc(1, sizeof(session_t)); - if (session == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session->id = 6; - session->sec_ver = 1; - session->pop = &pop; - session->weak = 1; - - // Start protocomm service - if (start_test_service(1, &pop) != ESP_OK) { - ESP_LOGE(TAG, "Error starting test"); - free(session); - return ESP_FAIL; - } - - // Intialise protocomm session with zero public keys - if (test_new_session(session) != ESP_OK) { - ESP_LOGE(TAG, "Error creating new session"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - // Perform 25519 security handshake with weak (zero) - // client public key, hence failing - if (test_sec_endpoint(session) == ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - // Sending request data to echo endpoint encrypted with zero - // public keys on both client and server side should fail - if (test_req_endpoint(session) == ESP_OK) { - ESP_LOGE(TAG, "Error testing request endpoint"); - stop_test_service(); - free(session); - return ESP_FAIL; - } - - stop_test_service(); - free(session); - - ESP_LOGI(TAG, "Protocomm test successful"); - return ESP_OK; -} - -static esp_err_t test_protocomm (session_t *session) -{ - ESP_LOGI(TAG, "Starting Protocomm test"); - - // Start protocomm service - if (start_test_service(session->sec_ver, session->pop) != ESP_OK) { - ESP_LOGE(TAG, "Error starting test"); - return ESP_FAIL; - } - - // Check version endpoint - if (test_ver_endpoint(session) != ESP_OK) { - ESP_LOGE(TAG, "Error testing version endpoint"); - stop_test_service(); - return ESP_FAIL; - } - - // Intialise protocomm session with zero public keys - if (test_new_session(session) != ESP_OK) { - ESP_LOGE(TAG, "Error creating new session"); - stop_test_service(); - return ESP_FAIL; - } - - // Perform 25519 security handshake to set public keys - if (test_sec_endpoint(session) != ESP_OK) { - ESP_LOGE(TAG, "Error testing security endpoint"); - stop_test_service(); - return ESP_FAIL; - } - - // Send request data to echo endpoint encrypted with - // the set public keys on both client and server side - if (test_req_endpoint(session) != ESP_OK) { - ESP_LOGE(TAG, "Error testing request endpoint"); - stop_test_service(); - return ESP_FAIL; - } - - // Stop protocomm service - stop_test_service(); - ESP_LOGI(TAG, "Protocomm test successful"); - return ESP_OK; -} - -static esp_err_t test_security1 (void) -{ - ESP_LOGI(TAG, "Starting Sec1 test"); - - const char *pop_data = "test pop"; - protocomm_security_pop_t pop = { - .data = (const uint8_t *)pop_data, - .len = strlen(pop_data) - }; - - session_t *session = calloc(1, sizeof(session_t)); - if (session == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session->id = 7; - session->sec_ver = 1; - session->pop = &pop; - - if (test_protocomm (session) != ESP_OK) { - ESP_LOGE(TAG, "Sec1 test failed"); - free(session); - return ESP_FAIL; - } - - ESP_LOGI(TAG, "Sec1 test successful"); - free(session); - return ESP_OK; -} - -static esp_err_t test_security0 (void) -{ - ESP_LOGI(TAG, "Starting Sec0 test"); - - session_t *session = calloc(1, sizeof(session_t)); - if (session == NULL) { - ESP_LOGE(TAG, "Error allocating session"); - return ESP_ERR_NO_MEM; - } - - session->id = 8; - session->sec_ver = 0; - session->pop = NULL; - - if (test_protocomm (session) != ESP_OK) { - ESP_LOGE(TAG, "Sec0 test failed"); - free(session); - return ESP_FAIL; - } - - ESP_LOGI(TAG, "Sec0 test successful"); - free(session); - return ESP_OK; -} - -TEST_CASE("leak test", "[PROTOCOMM]") -{ -#ifdef CONFIG_HEAP_TRACING - heap_trace_init_standalone(trace_record, NUM_RECORDS); - heap_trace_start(HEAP_TRACE_LEAKS); -#endif - - /* Run basic tests for the first time to allow for internal long - * time allocations to happen (not related to protocomm) */ - test_security0(); - test_security1(); - usleep(1000); - -#ifdef CONFIG_HEAP_TRACING - heap_trace_stop(); - heap_trace_dump(); -#endif - - /* Run all tests passively. Any leaks due - * to protocomm should show up now */ - unsigned pre_start_mem = esp_get_free_heap_size(); - - test_security0(); - test_security1(); - test_security1_no_encryption(); - test_security1_session_overflow(); - test_security1_wrong_pop(); - test_security1_insecure_client(); - test_security1_weak_session(); - - usleep(1000); - - unsigned post_stop_mem = esp_get_free_heap_size(); - - if (pre_start_mem != post_stop_mem) { - ESP_LOGE(TAG, "Mismatch in free heap size : %d bytes", post_stop_mem - pre_start_mem); - } - - TEST_ASSERT(pre_start_mem == post_stop_mem); -} - -TEST_CASE("security 0 basic test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security0() == ESP_OK); -} - -TEST_CASE("security 1 basic test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security1() == ESP_OK); -} - -TEST_CASE("security 1 no encryption test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security1_no_encryption() == ESP_OK); -} - -TEST_CASE("security 1 session overflow test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security1_session_overflow() == ESP_OK); -} - -TEST_CASE("security 1 wrong pop test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security1_wrong_pop() == ESP_OK); -} - -TEST_CASE("security 1 insecure client test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security1_insecure_client() == ESP_OK); -} - -TEST_CASE("security 1 weak session test", "[PROTOCOMM]") -{ - TEST_ASSERT(test_security1_weak_session() == ESP_OK); -} diff --git a/tools/unit-test-app/components/unity/ref_clock.c b/tools/unit-test-app/components/unity/ref_clock.c deleted file mode 100644 index 52d4f1ca..00000000 --- a/tools/unit-test-app/components/unity/ref_clock.c +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2017 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/* Unit tests need to have access to reliable timestamps even if CPU and APB - * clock frequencies change over time. This reference clock is built upon two - * peripherals: one RMT channel and one PCNT channel, plus one GPIO to connect - * these peripherals. - * - * RMT channel is configured to use REF_TICK as clock source, which is a 1 MHz - * clock derived from APB_CLK using a set of dividers. The divider is changed - * automatically by hardware depending on the current clock source of APB_CLK. - * For example, if APB_CLK is derived from PLL, one divider is used, and when - * APB_CLK is derived from XTAL, another divider is used. RMT channel clocked - * by REF_TICK is configured to generate a continuous 0.5 MHz signal, which is - * connected to a GPIO. PCNT takes the input signal from this GPIO and counts - * the edges (which occur at 1MHz frequency). PCNT counter is only 16 bit wide, - * so an interrupt is configured to trigger when the counter reaches 30000, - * incrementing a 32-bit millisecond counter maintained by software. - * Together these two counters may be used at any time to obtain the timestamp. - */ - -#include "test_utils.h" -#include "soc/rmt_struct.h" -#include "soc/pcnt_struct.h" -#include "soc/pcnt_reg.h" -#include "soc/gpio_sig_map.h" -#include "soc/dport_reg.h" -#include "rom/gpio.h" -#include "rom/ets_sys.h" -#include "driver/gpio.h" -#include "esp_intr_alloc.h" -#include "freertos/FreeRTOS.h" -#include "driver/periph_ctrl.h" - -/* Select which RMT and PCNT channels, and GPIO to use */ -#define REF_CLOCK_RMT_CHANNEL 7 -#define REF_CLOCK_PCNT_UNIT 0 -#define REF_CLOCK_GPIO 21 - -#define REF_CLOCK_PRESCALER_MS 30 - -static void IRAM_ATTR pcnt_isr(void* arg); - -static intr_handle_t s_intr_handle; -static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED; -static volatile uint32_t s_milliseconds; - -void ref_clock_init() -{ - assert(s_intr_handle == NULL && "already initialized"); - - // Route RMT output to GPIO matrix - gpio_matrix_out(REF_CLOCK_GPIO, RMT_SIG_OUT0_IDX + REF_CLOCK_RMT_CHANNEL, false, false); - - - // Initialize RMT - periph_module_enable(PERIPH_RMT_MODULE); - RMT.apb_conf.fifo_mask = 1; - rmt_item32_t data = { - .duration0 = 1, - .level0 = 1, - .duration1 = 0, - .level1 = 0 - }; - RMTMEM.chan[REF_CLOCK_RMT_CHANNEL].data32[0] = data; - RMTMEM.chan[REF_CLOCK_RMT_CHANNEL].data32[1].val = 0; - - - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf0.clk_en = 1; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.tx_start = 0; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.mem_owner = 0; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.mem_rd_rst = 1; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.apb_mem_rst = 1; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf0.carrier_en = 0; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf0.div_cnt = 1; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf0.mem_size = 1; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.ref_always_on = 0; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.tx_conti_mode = 1; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.tx_start = 1; - - // Route signal to PCNT - int pcnt_sig_idx = (REF_CLOCK_PCNT_UNIT < 5) ? - PCNT_SIG_CH0_IN0_IDX + 4 * REF_CLOCK_PCNT_UNIT : - PCNT_SIG_CH0_IN5_IDX + 4 * (REF_CLOCK_PCNT_UNIT - 5); - gpio_matrix_in(REF_CLOCK_GPIO, pcnt_sig_idx, false); - if (REF_CLOCK_GPIO != 20) { - PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[REF_CLOCK_GPIO]); - } else { - PIN_INPUT_ENABLE(PERIPHS_IO_MUX_GPIO20_U); - } - - // Initialize PCNT - periph_module_enable(PERIPH_PCNT_MODULE); - - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.ch0_hctrl_mode = 0; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.ch0_lctrl_mode = 0; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.ch0_pos_mode = 1; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.ch0_neg_mode = 1; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.thr_l_lim_en = 0; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.thr_h_lim_en = 1; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.thr_zero_en = 0; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.thr_thres0_en = 0; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf0.thr_thres1_en = 0; - PCNT.conf_unit[REF_CLOCK_PCNT_UNIT].conf2.cnt_h_lim = REF_CLOCK_PRESCALER_MS * 1000; - - // Enable PCNT and wait for it to start counting - PCNT.ctrl.val &= ~(BIT(REF_CLOCK_PCNT_UNIT * 2 + 1)); - PCNT.ctrl.val |= BIT(REF_CLOCK_PCNT_UNIT * 2); - PCNT.ctrl.val &= ~BIT(REF_CLOCK_PCNT_UNIT * 2); - - ets_delay_us(10000); - - // Enable interrupt - s_milliseconds = 0; - ESP_ERROR_CHECK(esp_intr_alloc(ETS_PCNT_INTR_SOURCE, ESP_INTR_FLAG_IRAM, pcnt_isr, NULL, &s_intr_handle)); - PCNT.int_clr.val = BIT(REF_CLOCK_PCNT_UNIT); - PCNT.int_ena.val = BIT(REF_CLOCK_PCNT_UNIT); -} - -static void IRAM_ATTR pcnt_isr(void* arg) -{ - portENTER_CRITICAL(&s_lock); - PCNT.int_clr.val = BIT(REF_CLOCK_PCNT_UNIT); - s_milliseconds += REF_CLOCK_PRESCALER_MS; - portEXIT_CRITICAL(&s_lock); -} - -void ref_clock_deinit() -{ - assert(s_intr_handle && "deinit called without init"); - - // Disable interrupt - PCNT.int_ena.val &= ~BIT(REF_CLOCK_PCNT_UNIT); - esp_intr_free(s_intr_handle); - s_intr_handle = NULL; - - // Disable RMT - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf1.tx_start = 0; - RMT.conf_ch[REF_CLOCK_RMT_CHANNEL].conf0.clk_en = 0; - periph_module_disable(PERIPH_RMT_MODULE); - - // Disable PCNT - PCNT.ctrl.val |= ~(BIT(REF_CLOCK_PCNT_UNIT * 2 + 1)); - periph_module_disable(PERIPH_PCNT_MODULE); -} - -uint64_t ref_clock_get() -{ - portENTER_CRITICAL(&s_lock); - uint32_t microseconds = PCNT.cnt_unit[REF_CLOCK_PCNT_UNIT].cnt_val; - uint32_t milliseconds = s_milliseconds; - if (PCNT.int_st.val & BIT(REF_CLOCK_PCNT_UNIT)) { - // refresh counter value, in case the overflow has happened after reading cnt_val - microseconds = PCNT.cnt_unit[REF_CLOCK_PCNT_UNIT].cnt_val; - milliseconds += REF_CLOCK_PRESCALER_MS; - } - portEXIT_CRITICAL(&s_lock); - return 1000 * (uint64_t) milliseconds + (uint64_t) microseconds; -} diff --git a/tools/unit-test-app/components/unity/unity_platform.c b/tools/unit-test-app/components/unity/unity_platform.c index 02fcd887..0eda486e 100644 --- a/tools/unit-test-app/components/unity/unity_platform.c +++ b/tools/unit-test-app/components/unity/unity_platform.c @@ -96,7 +96,7 @@ void tearDown(void) Unity.TestFile = __FILE__; /* check if unit test has caused heap corruption in any heap */ - TEST_ASSERT_MESSAGE( heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap"); + //TEST_ASSERT_MESSAGE( heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap"); /* check for leaks */ #ifdef CONFIG_HEAP_TRACING diff --git a/tools/unit-test-app/configs/aes_no_hw b/tools/unit-test-app/configs/aes_no_hw deleted file mode 100644 index f890e3f8..00000000 --- a/tools/unit-test-app/configs/aes_no_hw +++ /dev/null @@ -1,3 +0,0 @@ -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update -TEST_COMPONENTS=mbedtls -CONFIG_MBEDTLS_HARDWARE_AES=n diff --git a/tools/unit-test-app/configs/app_update b/tools/unit-test-app/configs/app_update deleted file mode 100644 index 4069be24..00000000 --- a/tools/unit-test-app/configs/app_update +++ /dev/null @@ -1,13 +0,0 @@ -TEST_COMPONENTS=app_update -TEST_EXCLUDE_COMPONENTS=libsodium bt -CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_two_ota.csv" -CONFIG_PARTITION_TABLE_FILENAME="partition_table_unit_test_two_ota.csv" -CONFIG_PARTITION_TABLE_OFFSET=0x18000 -CONFIG_BOOTLOADER_FACTORY_RESET=y -CONFIG_BOOTLOADER_APP_TEST=y -CONFIG_BOOTLOADER_HOLD_TIME_GPIO=2 -CONFIG_BOOTLOADER_OTA_DATA_ERASE=y -CONFIG_BOOTLOADER_NUM_PIN_FACTORY_RESET=4 -CONFIG_BOOTLOADER_NUM_PIN_APP_TEST=32 \ No newline at end of file diff --git a/tools/unit-test-app/configs/bt b/tools/unit-test-app/configs/bt deleted file mode 100644 index 9bb48647..00000000 --- a/tools/unit-test-app/configs/bt +++ /dev/null @@ -1,4 +0,0 @@ -TEST_COMPONENTS=bt -TEST_EXCLUDE_COMPONENTS=app_update -CONFIG_BT_ENABLED=y -CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 \ No newline at end of file diff --git a/tools/unit-test-app/configs/default b/tools/unit-test-app/configs/default deleted file mode 100644 index f7f508a4..00000000 --- a/tools/unit-test-app/configs/default +++ /dev/null @@ -1 +0,0 @@ -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update \ No newline at end of file diff --git a/tools/unit-test-app/configs/libsodium b/tools/unit-test-app/configs/libsodium deleted file mode 100644 index af090a53..00000000 --- a/tools/unit-test-app/configs/libsodium +++ /dev/null @@ -1,3 +0,0 @@ -TEST_COMPONENTS=libsodium -TEST_EXCLUDE_COMPONENTS=bt app_update -CONFIG_UNITY_FREERTOS_STACK_SIZE=12288 \ No newline at end of file diff --git a/tools/unit-test-app/configs/psram b/tools/unit-test-app/configs/psram deleted file mode 100644 index 541bff57..00000000 --- a/tools/unit-test-app/configs/psram +++ /dev/null @@ -1,3 +0,0 @@ -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update driver esp32 spi_flash -CONFIG_SPIRAM_SUPPORT=y -CONFIG_SPIRAM_BANKSWITCH_ENABLE=n diff --git a/tools/unit-test-app/configs/psram_2 b/tools/unit-test-app/configs/psram_2 deleted file mode 100644 index 4173606d..00000000 --- a/tools/unit-test-app/configs/psram_2 +++ /dev/null @@ -1,3 +0,0 @@ -TEST_COMPONENTS=driver esp32 spi_flash -CONFIG_SPIRAM_SUPPORT=y -CONFIG_SPIRAM_BANKSWITCH_ENABLE=n diff --git a/tools/unit-test-app/configs/psram_8m b/tools/unit-test-app/configs/psram_8m deleted file mode 100644 index d28e5870..00000000 --- a/tools/unit-test-app/configs/psram_8m +++ /dev/null @@ -1,4 +0,0 @@ -TEST_COMPONENTS=esp32 -CONFIG_SPIRAM_SUPPORT=y -CONFIG_SPIRAM_BANKSWITCH_ENABLE=y -CONFIG_SPIRAM_BANKSWITCH_RESERVE=8 diff --git a/tools/unit-test-app/configs/release b/tools/unit-test-app/configs/release deleted file mode 100644 index 86fbc9a4..00000000 --- a/tools/unit-test-app/configs/release +++ /dev/null @@ -1,3 +0,0 @@ -TEST_EXCLUDE_COMPONENTS=bt app_update -CONFIG_OPTIMIZATION_LEVEL_RELEASE=y -CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=y \ No newline at end of file diff --git a/tools/unit-test-app/configs/single_core b/tools/unit-test-app/configs/single_core deleted file mode 100644 index 9c85abae..00000000 --- a/tools/unit-test-app/configs/single_core +++ /dev/null @@ -1,4 +0,0 @@ -TEST_EXCLUDE_COMPONENTS=libsodium bt app_update -CONFIG_MEMMAP_SMP=n -CONFIG_FREERTOS_UNICORE=y -CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y diff --git a/tools/unit-test-app/idf_ext.py b/tools/unit-test-app/idf_ext.py deleted file mode 100644 index 9bc9ae00..00000000 --- a/tools/unit-test-app/idf_ext.py +++ /dev/null @@ -1,279 +0,0 @@ -import sys -import glob -import tempfile -import os -import os.path -import re -import shutil -import argparse -import json -import copy - -PROJECT_NAME = "unit-test-app" -PROJECT_PATH = os.getcwd() - -# List of unit-test-app configurations. -# Each file in configs/ directory defines a configuration. The format is the -# same as sdkconfig file. Configuration is applied on top of sdkconfig.defaults -# file from the project directory -CONFIG_NAMES = os.listdir(os.path.join(PROJECT_PATH, "configs")) - -# Build (intermediate) and output (artifact) directories -BUILDS_DIR = os.path.join(PROJECT_PATH, "builds") -BINARIES_DIR = os.path.join(PROJECT_PATH, "output") - -# Convert the values passed to the -T parameter to corresponding cache entry definitions -# TESTS_ALL and TEST_COMPONENTS -class TestComponentAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - # Create a new of cache definition entry, adding previous elements - cache_entries = list() - - existing_entries = getattr(namespace, "define_cache_entry", []) - - if existing_entries: - cache_entries.extend(existing_entries) - - # Form -D arguments - if "all" in values: - cache_entries.append("TESTS_ALL=1") - cache_entries.append("TEST_COMPONENTS=''") - else: - cache_entries.append("TESTS_ALL=0") - cache_entries.append("TEST_COMPONENTS='%s'" % " ".join(values)) - - setattr(namespace, "define_cache_entry", cache_entries) - - # Brute force add reconfigure at the very beginning - existing_actions = getattr(namespace, "actions", []) - if not "reconfigure" in existing_actions: - existing_actions = ["reconfigure"] + existing_actions - setattr(namespace, "actions", existing_actions) - -class TestExcludeComponentAction(argparse.Action): - def __call__(self, parser, namespace, values, option_string=None): - # Create a new of cache definition entry, adding previous elements - cache_entries = list() - - existing_entries = getattr(namespace, "define_cache_entry", []) - - if existing_entries: - cache_entries.extend(existing_entries) - - cache_entries.append("TEST_EXCLUDE_COMPONENTS='%s'" % " ".join(values)) - - setattr(namespace, "define_cache_entry", cache_entries) - - # Brute force add reconfigure at the very beginning - existing_actions = getattr(namespace, "actions", []) - if not "reconfigure" in existing_actions: - existing_actions = ["reconfigure"] + existing_actions - setattr(namespace, "actions", existing_actions) - -def add_argument_extensions(parser): - # For convenience, define a -T argument that gets converted to -D arguments - parser.add_argument('-T', '--test-component', help="Specify the components to test", nargs='+', action=TestComponentAction) - # For convenience, define a -T argument that gets converted to -D arguments - parser.add_argument('-E', '--test-exclude-components', help="Specify the components to exclude from testing", nargs='+', action=TestExcludeComponentAction) - -def add_action_extensions(base_functions, base_actions): - - def ut_apply_config(ut_apply_config_name, args): - config_name = re.match(r"ut-apply-config-(.*)", ut_apply_config_name).group(1) - - def set_config_build_variables(prop, defval = None): - property_value = re.match(r"^%s=(.*)" % prop, config_file_content) - if (property_value): - property_value = property_value.group(1) - else: - property_value = defval - - if (property_value): - try: - args.define_cache_entry.append("%s=" % prop + property_value) - except AttributeError: - args.define_cache_entry = ["%s=" % prop + property_value] - - return property_value - - sdkconfig_set = None - - if args.define_cache_entry: - sdkconfig_set = filter(lambda s: "SDKCONFIG=" in s, args.define_cache_entry) - - sdkconfig_path = os.path.join(args.project_dir, "sdkconfig") - - if sdkconfig_set: - sdkconfig_path = sdkconfig_set[-1].split("=")[1] - sdkconfig_path = os.path.abspath(sdkconfig_path) - - try: - os.remove(sdkconfig_path) - except OSError: - pass - - if config_name in CONFIG_NAMES: - # Parse the sdkconfig for components to be included/excluded and tests to be run - config = os.path.join(PROJECT_PATH, "configs", config_name) - - with open(config, "r") as config_file: - config_file_content = config_file.read() - - set_config_build_variables("EXCLUDE_COMPONENTS", "''") - - test_components = set_config_build_variables("TEST_COMPONENTS", "''") - - tests_all = None - if test_components == "''": - tests_all = "TESTS_ALL=1" - else: - tests_all = "TESTS_ALL=0" - - try: - args.define_cache_entry.append(tests_all) - except AttributeError: - args.define_cache_entry = [tests_all] - - set_config_build_variables("TEST_EXCLUDE_COMPONENTS","''") - - with tempfile.NamedTemporaryFile() as sdkconfig_temp: - # Use values from the combined defaults and the values from - # config folder to build config - sdkconfig_default = os.path.join(PROJECT_PATH, "sdkconfig.defaults") - - with open(sdkconfig_default, "rb") as sdkconfig_default_file: - sdkconfig_temp.write(sdkconfig_default_file.read()) - - sdkconfig_config = os.path.join(PROJECT_PATH, "configs", config_name) - with open(sdkconfig_config, "rb") as sdkconfig_config_file: - sdkconfig_temp.write(b"\n") - sdkconfig_temp.write(sdkconfig_config_file.read()) - - sdkconfig_temp.flush() - - try: - args.define_cache_entry.append("SDKCONFIG_DEFAULTS=" + sdkconfig_temp.name) - except AttributeError: - args.define_cache_entry = ["SDKCONFIG_DEFAULTS=" + sdkconfig_temp.name] - - reconfigure = base_functions["reconfigure"] - reconfigure(None, args) - else: - if not config_name == "all-configs": - print("unknown unit test app config for action '%s'" % ut_apply_config_name) - - # This target builds the configuration. It does not currently track dependencies, - # but is good enough for CI builds if used together with clean-all-configs. - # For local builds, use 'apply-config-NAME' target and then use normal 'all' - # and 'flash' targets. - def ut_build(ut_build_name, args): - # Create a copy of the passed arguments to prevent arg modifications to accrue if - # all configs are being built - build_args = copy.copy(args) - - config_name = re.match(r"ut-build-(.*)", ut_build_name).group(1) - - if config_name in CONFIG_NAMES: - build_args.build_dir = os.path.join(BUILDS_DIR, config_name) - - src = os.path.join(BUILDS_DIR, config_name) - dest = os.path.join(BINARIES_DIR, config_name) - - try: - os.makedirs(dest) - except OSError: - pass - - # Build, tweaking paths to sdkconfig and sdkconfig.defaults - ut_apply_config("ut-apply-config-" + config_name, build_args) - - build_target = base_functions["build_target"] - - build_target("all", build_args) - - # Copy artifacts to the output directory - shutil.copyfile(os.path.join(build_args.project_dir, "sdkconfig"), os.path.join(dest, "sdkconfig")) - - binaries = [PROJECT_NAME + x for x in [".elf", ".bin", ".map"]] - - for binary in binaries: - shutil.copyfile(os.path.join(src, binary), os.path.join(dest, binary)) - - try: - os.mkdir(os.path.join(dest, "bootloader")) - except OSError: - pass - - shutil.copyfile(os.path.join(src, "bootloader", "bootloader.bin"), os.path.join(dest, "bootloader", "bootloader.bin")) - - for partition_table in glob.glob(os.path.join(src, "partition_table", "partition-table*.bin")): - try: - os.mkdir(os.path.join(dest, "partition_table")) - except OSError: - pass - shutil.copyfile(partition_table, os.path.join(dest, "partition_table", os.path.basename(partition_table))) - - shutil.copyfile(os.path.join(src, "flash_project_args"), os.path.join(dest, "flash_project_args")) - - binaries = glob.glob(os.path.join(src, "*.bin")) - binaries = [os.path.basename(s) for s in binaries] - - for binary in binaries: - shutil.copyfile(os.path.join(src, binary), os.path.join(dest, binary)) - - else: - if not config_name == "all-configs": - print("unknown unit test app config for action '%s'" % ut_build_name) - - def ut_clean(ut_clean_name, args): - config_name = re.match(r"ut-clean-(.*)", ut_clean_name).group(1) - if config_name in CONFIG_NAMES: - shutil.rmtree(os.path.join(BUILDS_DIR, config_name), ignore_errors=True) - shutil.rmtree(os.path.join(BINARIES_DIR, config_name), ignore_errors=True) - else: - if not config_name == "all-configs": - print("unknown unit test app config for action '%s'" % ut_clean_name) - - def ut_help(action, args): - HELP_STRING = """ -Additional unit-test-app specific targets - -idf.py ut-build-NAME - Build unit-test-app with configuration provided in configs/NAME. - Build directory will be builds/NAME/, output binaries will be - under output/NAME/ - -idf.py ut-clean-NAME - Remove build and output directories for configuration NAME. - -idf.py ut-build-all-configs - Build all configurations defined in configs/ directory. - -idf.py ut-apply-config-NAME - Generates configuration based on configs/NAME in sdkconfig - file. After this, normal all/flash targets can be used. - Useful for development/debugging. -""" - print(HELP_STRING) - - # Build dictionary of action extensions - extensions = dict() - - # This generates per-config targets (clean, build, apply-config). - build_all_config_deps = [] - clean_all_config_deps = [] - - for config in CONFIG_NAMES: - config_build_action_name = "ut-build-" + config - config_clean_action_name = "ut-clean-" + config - config_apply_config_action_name = "ut-apply-config-" + config - - extensions[config_build_action_name] = (ut_build, [], []) - extensions[config_clean_action_name] = (ut_clean, [], []) - extensions[config_apply_config_action_name] = (ut_apply_config, [], []) - - build_all_config_deps.append(config_build_action_name) - clean_all_config_deps.append(config_clean_action_name) - - extensions["ut-build-all-configs"] = (ut_build, build_all_config_deps, []) - extensions["ut-clean-all-configs"] = (ut_clean, clean_all_config_deps, []) - - extensions["ut-help"] = (ut_help, [], []) - - base_actions.update(extensions) diff --git a/tools/unit-test-app/partition_table_unit_test_app.csv b/tools/unit-test-app/partition_table_unit_test_app.csv index 432553f2..e597a011 100644 --- a/tools/unit-test-app/partition_table_unit_test_app.csv +++ b/tools/unit-test-app/partition_table_unit_test_app.csv @@ -5,13 +5,10 @@ nvs, data, nvs, 0x9000, 0x4000 otadata, data, ota, 0xd000, 0x2000 phy_init, data, phy, 0xf000, 0x1000 -factory, 0, 0, 0x10000, 0x240000 -# these OTA partitions are used for tests, but can't fit real OTA apps in them -# (done this way to reduce total flash usage.) +factory, 0, 0, 0x10000, 0xF0000 ota_0, 0, ota_0, , 64K ota_1, 0, ota_1, , 64K # flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests flash_test, data, fat, , 528K -nvs_key, data, nvs_keys, , 0x1000, encrypted # Note: still 1MB of a 4MB flash left free for some other purpose diff --git a/tools/unit-test-app/partition_table_unit_test_two_ota.csv b/tools/unit-test-app/partition_table_unit_test_two_ota.csv deleted file mode 100644 index 7c698c17..00000000 --- a/tools/unit-test-app/partition_table_unit_test_two_ota.csv +++ /dev/null @@ -1,11 +0,0 @@ -# Special partition table for unit test app_update -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, , 0x4000 -otadata, data, ota, , 0x2000 -phy_init, data, phy, , 0x1000 -factory, 0, 0, , 0xB0000 -ota_0, 0, ota_0, , 0xB0000 -ota_1, 0, ota_1, , 0xB0000 -test, 0, test, , 0xB0000 -# flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests -flash_test, data, fat, , 528K \ No newline at end of file diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index badf01bb..ab100149 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -1,30 +1,7 @@ -CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y -CONFIG_ESPTOOLPY_BAUD_921600B=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=n CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partition_table_unit_test_app.csv" CONFIG_PARTITION_TABLE_FILENAME="partition_table_unit_test_app.csv" CONFIG_PARTITION_TABLE_OFFSET=0x8000 -CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y -CONFIG_ESP32_XTAL_FREQ_AUTO=y -CONFIG_FREERTOS_HZ=1000 -CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y -CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 -CONFIG_FREERTOS_USE_TRACE_FACILITY=y -CONFIG_HEAP_POISONING_COMPREHENSIVE=y -CONFIG_MBEDTLS_HARDWARE_MPI=y -CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y -CONFIG_MBEDTLS_HARDWARE_SHA=y -CONFIG_SPI_FLASH_ENABLE_COUNTERS=y -CONFIG_ULP_COPROC_ENABLED=y -CONFIG_TASK_WDT=n -CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=y -CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=7 -CONFIG_STACK_CHECK_STRONG=y -CONFIG_STACK_CHECK=y -CONFIG_SUPPORT_STATIC_ALLOCATION=y -CONFIG_ESP_TIMER_PROFILING=y -CONFIG_ADC2_DISABLE_DAC=n -CONFIG_WARN_WRITE_STRINGS=y -CONFIG_SPI_MASTER_IN_IRAM=y \ No newline at end of file +CONFIG_TASK_WDT= +CONFIG_ENABLE_PTHREAD=y + diff --git a/tools/unit-test-app/tools/ConfigDependency.yml b/tools/unit-test-app/tools/ConfigDependency.yml deleted file mode 100644 index f7b265bc..00000000 --- a/tools/unit-test-app/tools/ConfigDependency.yml +++ /dev/null @@ -1,2 +0,0 @@ -"psram": '{CONFIG_SPIRAM_SUPPORT=y} and not {CONFIG_SPIRAM_BANKSWITCH_ENABLE=y}' -"8Mpsram": "CONFIG_SPIRAM_BANKSWITCH_ENABLE=y" diff --git a/tools/unit-test-app/tools/CreateSectionTable.py b/tools/unit-test-app/tools/CreateSectionTable.py deleted file mode 100644 index ca5114c6..00000000 --- a/tools/unit-test-app/tools/CreateSectionTable.py +++ /dev/null @@ -1,163 +0,0 @@ -# This file is used to process section data generated by `objdump -s` -import re - - -class Section(object): - """ - One Section of section table. contains info about section name, address and raw data - """ - SECTION_START_PATTERN = re.compile(b"Contents of section (.+?):") - DATA_PATTERN = re.compile(b"([0-9a-f]{4,8})") - - def __init__(self, name, start_address, data): - self.name = name - self.start_address = start_address - self.data = data - - def __contains__(self, item): - """ check if the section name and address match this section """ - if (item["section"] == self.name or item["section"] == "any") \ - and (self.start_address <= item["address"] < (self.start_address + len(self.data))): - return True - else: - return False - - def __getitem__(self, item): - """ - process slice. - convert absolute address to relative address in current section and return slice result - """ - if isinstance(item, int): - return self.data[item - self.start_address] - elif isinstance(item, slice): - start = item.start if item.start is None else item.start - self.start_address - stop = item.stop if item.stop is None else item.stop - self.start_address - return self.data[start:stop] - return self.data[item] - - def __str__(self): - return "%s [%08x - %08x]" % (self.name, self.start_address, self.start_address + len(self.data)) - - __repr__ = __str__ - - @classmethod - def parse_raw_data(cls, raw_data): - """ - process raw data generated by `objdump -s`, create section and return un-processed lines - :param raw_data: lines of raw data generated by `objdump -s` - :return: one section, un-processed lines - """ - name = "" - data = "" - start_address = 0 - # first find start line - for i, line in enumerate(raw_data): - if b"Contents of section " in line: # do strcmp first to speed up - match = cls.SECTION_START_PATTERN.search(line) - if match is not None: - name = match.group(1) - raw_data = raw_data[i + 1:] - break - else: - # do some error handling - raw_data = [b""] # add a dummy first data line - - def process_data_line(line_to_process): - # first remove the ascii part - hex_part = line_to_process.split(b" ")[0] - # process rest part - data_list = cls.DATA_PATTERN.findall(hex_part) - try: - _address = int(data_list[0], base=16) - except IndexError: - _address = -1 - - def hex_to_str(hex_data): - if len(hex_data) % 2 == 1: - hex_data = b"0" + hex_data # append zero at the beginning - _length = len(hex_data) - return "".join([chr(int(hex_data[_i:_i + 2], base=16)) - for _i in range(0, _length, 2)]) - - return _address, "".join([hex_to_str(x) for x in data_list[1:]]) - - # handle first line: - address, _data = process_data_line(raw_data[0]) - if address != -1: - start_address = address - data += _data - raw_data = raw_data[1:] - for i, line in enumerate(raw_data): - address, _data = process_data_line(line) - if address == -1: - raw_data = raw_data[i:] - break - else: - data += _data - else: - # do error handling - raw_data = [] - - section = cls(name, start_address, data) if start_address != -1 else None - unprocessed_data = None if len(raw_data) == 0 else raw_data - return section, unprocessed_data - - -class SectionTable(object): - """ elf section table """ - - def __init__(self, file_name): - with open(file_name, "rb") as f: - raw_data = f.readlines() - self.table = [] - while raw_data: - section, raw_data = Section.parse_raw_data(raw_data) - self.table.append(section) - - def get_unsigned_int(self, section, address, size=4, endian="LE"): - """ - get unsigned int from section table - :param section: section name; use "any" will only match with address - :param address: start address - :param size: size in bytes - :param endian: LE or BE - :return: int or None - """ - if address % 4 != 0 or size % 4 != 0: - print("warning: try to access without 4 bytes aligned") - key = {"address": address, "section": section} - for section in self.table: - if key in section: - tmp = section[address:address+size] - value = 0 - for i in range(size): - if endian == "LE": - value += ord(tmp[i]) << (i*8) - elif endian == "BE": - value += ord(tmp[i]) << ((size - i - 1) * 8) - else: - print("only support LE or BE for parameter endian") - assert False - break - else: - value = None - return value - - def get_string(self, section, address): - """ - get string ('\0' terminated) from section table - :param section: section name; use "any" will only match with address - :param address: start address - :return: string or None - """ - value = None - key = {"address": address, "section": section} - for section in self.table: - if key in section: - value = section[address:] - for i, c in enumerate(value): - if c == '\0': - value = value[:i] - break - break - return value diff --git a/tools/unit-test-app/tools/ModuleDefinition.yml b/tools/unit-test-app/tools/ModuleDefinition.yml deleted file mode 100644 index 0f9a31f9..00000000 --- a/tools/unit-test-app/tools/ModuleDefinition.yml +++ /dev/null @@ -1,127 +0,0 @@ -freertos: - module: System - module abbr: SYS - sub module: OS - sub module abbr: OS -nvs: - module: System - module abbr: SYS - sub module: NVS - sub module abbr: NVS -partition: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -ulp: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -fp: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -hw: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -tjpgd: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -miniz: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -mmap: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -bignum: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -newlib: - module: System - module abbr: SYS - sub module: Std Lib - sub module abbr: STD -aes: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -mbedtls: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -spi_flash: - module: Driver - module abbr: DRV - sub module: SPI - sub module abbr: SPI -spi_flash_read: - module: Driver - module abbr: DRV - sub module: SPI - sub module abbr: SPI -spi_flash_write: - module: Driver - module abbr: DRV - sub module: SPI - sub module abbr: SPI -esp32: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -deepsleep: - module: RTC - module abbr: RTC - sub module: Deep Sleep - sub module abbr: SLEEP -sd: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -cxx: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -fatfs: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -delay: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -spi: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -vfs: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC -misc: - module: System - module abbr: SYS - sub module: Misc - sub module abbr: MISC - - diff --git a/tools/unit-test-app/tools/TagDefinition.yml b/tools/unit-test-app/tools/TagDefinition.yml deleted file mode 100644 index f84d9a70..00000000 --- a/tools/unit-test-app/tools/TagDefinition.yml +++ /dev/null @@ -1,20 +0,0 @@ -ignore: - # if the type exist but no value assigned - default: "Yes" - # if the type is not exist in tag list - omitted: "No" -test_env: - default: "UT_T1_1" - omitted: "UT_T1_1" -reset: - default: "POWERON_RESET" - omitted: " " -multi_device: - default: "Yes" - omitted: "No" -multi_stage: - default: "Yes" - omitted: "No" -timeout: - default: 30 - omitted: 30 diff --git a/tools/unit-test-app/tools/UnitTestParser.py b/tools/unit-test-app/tools/UnitTestParser.py deleted file mode 100644 index b4c11c2a..00000000 --- a/tools/unit-test-app/tools/UnitTestParser.py +++ /dev/null @@ -1,318 +0,0 @@ -from __future__ import print_function -import yaml -import os -import re -import shutil -import subprocess - -from copy import deepcopy -import CreateSectionTable - -TEST_CASE_PATTERN = { - "initial condition": "UTINIT1", - "SDK": "ESP32_IDF", - "level": "Unit", - "execution time": 0, - "auto test": "Yes", - "category": "Function", - "test point 1": "basic function", - "version": "v1 (2016-12-06)", - "test environment": "UT_T1_1", - "reset": "", - "expected result": "1. set succeed", - "cmd set": "test_unit_test_case", - "Test App": "UT", -} - - -class Parser(object): - """ parse unit test cases from build files and create files for test bench """ - - TAG_PATTERN = re.compile("([^=]+)(=)?(.+)?") - DESCRIPTION_PATTERN = re.compile("\[([^]\[]+)\]") - CONFIG_PATTERN = re.compile(r"{([^}]+)}") - - # file path (relative to idf path) - TAG_DEF_FILE = os.path.join("tools", "unit-test-app", "tools", "TagDefinition.yml") - MODULE_DEF_FILE = os.path.join("tools", "unit-test-app", "tools", "ModuleDefinition.yml") - CONFIG_DEPENDENCY_FILE = os.path.join("tools", "unit-test-app", "tools", "ConfigDependency.yml") - MODULE_ARTIFACT_FILE = os.path.join("components", "idf_test", "ModuleDefinition.yml") - TEST_CASE_FILE = os.path.join("components", "idf_test", "unit_test", "TestCaseAll.yml") - UT_BIN_FOLDER = os.path.join("tools", "unit-test-app", "output") - ELF_FILE = "unit-test-app.elf" - SDKCONFIG_FILE = "sdkconfig" - - def __init__(self, idf_path=os.getenv("IDF_PATH")): - self.test_env_tags = {} - self.unit_jobs = {} - self.file_name_cache = {} - self.idf_path = idf_path - self.tag_def = yaml.load(open(os.path.join(idf_path, self.TAG_DEF_FILE), "r")) - self.module_map = yaml.load(open(os.path.join(idf_path, self.MODULE_DEF_FILE), "r")) - self.config_dependencies = yaml.load(open(os.path.join(idf_path, self.CONFIG_DEPENDENCY_FILE), "r")) - # used to check if duplicated test case names - self.test_case_names = set() - self.parsing_errors = [] - - def parse_test_cases_for_one_config(self, config_output_folder, config_name): - """ - parse test cases from elf and save test cases need to be executed to unit test folder - :param config_output_folder: build folder of this config - :param config_name: built unit test config name - """ - elf_file = os.path.join(config_output_folder, self.ELF_FILE) - subprocess.check_output('xtensa-esp32-elf-objdump -t {} | grep test_desc > case_address.tmp'.format(elf_file), - shell=True) - subprocess.check_output('xtensa-esp32-elf-objdump -s {} > section_table.tmp'.format(elf_file), shell=True) - - table = CreateSectionTable.SectionTable("section_table.tmp") - tags = self.parse_tags(os.path.join(config_output_folder, self.SDKCONFIG_FILE)) - test_cases = [] - with open("case_address.tmp", "rb") as f: - for line in f: - # process symbol table like: "3ffb4310 l O .dram0.data 00000018 test_desc_33$5010" - line = line.split() - test_addr = int(line[0], 16) - section = line[3] - - name_addr = table.get_unsigned_int(section, test_addr, 4) - desc_addr = table.get_unsigned_int(section, test_addr + 4, 4) - file_name_addr = table.get_unsigned_int(section, test_addr + 12, 4) - function_count = table.get_unsigned_int(section, test_addr+20, 4) - name = table.get_string("any", name_addr) - desc = table.get_string("any", desc_addr) - file_name = table.get_string("any", file_name_addr) - tc = self.parse_one_test_case(name, desc, file_name, config_name, tags) - - # check if duplicated case names - # we need to use it to select case, - # if duplicated IDs, Unity could select incorrect case to run - # and we need to check all cases no matter if it's going te be executed by CI - # also add app_name here, we allow same case for different apps - if (tc["summary"] + config_name) in self.test_case_names: - self.parsing_errors.append("duplicated test case ID: " + tc["summary"]) - else: - self.test_case_names.add(tc["summary"] + config_name) - - if tc["CI ready"] == "Yes": - # update test env list and the cases of same env list - if tc["test environment"] in self.test_env_tags: - self.test_env_tags[tc["test environment"]].append(tc["ID"]) - else: - self.test_env_tags.update({tc["test environment"]: [tc["ID"]]}) - - if function_count > 1: - tc.update({"child case num": function_count}) - - # only add cases need to be executed - test_cases.append(tc) - - os.remove("section_table.tmp") - os.remove("case_address.tmp") - - return test_cases - - def parse_case_properities(self, tags_raw): - """ - parse test case tags (properities) with the following rules: - * first tag is always group of test cases, it's mandatory - * the rest tags should be [type=value]. - * if the type have default value, then [type] equal to [type=default_value]. - * if the type don't don't exist, then equal to [type=omitted_value] - default_value and omitted_value are defined in TagDefinition.yml - :param tags_raw: raw tag string - :return: tag dict - """ - tags = self.DESCRIPTION_PATTERN.findall(tags_raw) - assert len(tags) > 0 - p = dict([(k, self.tag_def[k]["omitted"]) for k in self.tag_def]) - p["module"] = tags[0] - - if p["module"] not in self.module_map: - p["module"] = "misc" - - # parsing rest tags, [type=value], =value is optional - for tag in tags[1:]: - match = self.TAG_PATTERN.search(tag) - assert match is not None - tag_type = match.group(1) - tag_value = match.group(3) - if match.group(2) == "=" and tag_value is None: - # [tag_type=] means tag_value is empty string - tag_value = "" - if tag_type in p: - if tag_value is None: - p[tag_type] = self.tag_def[tag_type]["default"] - else: - p[tag_type] = tag_value - else: - # ignore not defined tag type - pass - return p - - @staticmethod - def parse_tags_internal(sdkconfig, config_dependencies, config_pattern): - required_tags = [] - - def compare_config(config): - return config in sdkconfig - - def process_condition(condition): - matches = config_pattern.findall(condition) - if matches: - for config in matches: - compare_result = compare_config(config) - # replace all configs in condition with True or False according to compare result - condition = re.sub(config_pattern, str(compare_result), condition, count=1) - # Now the condition is a python condition, we can use eval to compute its value - ret = eval(condition) - else: - # didn't use complex condition. only defined one condition for the tag - ret = compare_config(condition) - return ret - - for tag in config_dependencies: - if process_condition(config_dependencies[tag]): - required_tags.append(tag) - - return required_tags - - def parse_tags(self, sdkconfig_file): - """ - Some test configs could requires different DUTs. - For example, if CONFIG_SPIRAM_SUPPORT is enabled, we need WROVER-Kit to run test. - This method will get tags for runners according to ConfigDependency.yml(maps tags to sdkconfig). - - We support to the following syntax:: - - # define the config which requires the tag - 'tag_a': 'config_a="value_a"' - # define the condition for the tag - 'tag_b': '{config A} and (not {config B} or (not {config C} and {config D}))' - - :param sdkconfig_file: sdk config file of the unit test config - :return: required tags for runners - """ - - with open(sdkconfig_file, "r") as f: - configs_raw_data = f.read() - - configs = configs_raw_data.splitlines(False) - - return self.parse_tags_internal(configs, self.config_dependencies, self.CONFIG_PATTERN) - - def parse_one_test_case(self, name, description, file_name, config_name, tags): - """ - parse one test case - :param name: test case name (summary) - :param description: test case description (tag string) - :param file_name: the file defines this test case - :param config_name: built unit test app name - :param tags: tags to select runners - :return: parsed test case - """ - prop = self.parse_case_properities(description) - - test_case = deepcopy(TEST_CASE_PATTERN) - test_case.update({"config": config_name, - "module": self.module_map[prop["module"]]['module'], - "CI ready": "No" if prop["ignore"] == "Yes" else "Yes", - "ID": name, - "test point 2": prop["module"], - "steps": name, - "test environment": prop["test_env"], - "reset": prop["reset"], - "sub module": self.module_map[prop["module"]]['sub module'], - "summary": name, - "multi_device": prop["multi_device"], - "multi_stage": prop["multi_stage"], - "timeout": int(prop["timeout"]), - "tags": tags}) - return test_case - - def dump_test_cases(self, test_cases): - """ - dump parsed test cases to YAML file for test bench input - :param test_cases: parsed test cases - """ - with open(os.path.join(self.idf_path, self.TEST_CASE_FILE), "w+") as f: - yaml.dump({"test cases": test_cases}, f, allow_unicode=True, default_flow_style=False) - - def copy_module_def_file(self): - """ copy module def file to artifact path """ - src = os.path.join(self.idf_path, self.MODULE_DEF_FILE) - dst = os.path.join(self.idf_path, self.MODULE_ARTIFACT_FILE) - shutil.copy(src, dst) - - def parse_test_cases(self): - """ parse test cases from multiple built unit test apps """ - test_cases = [] - - output_folder = os.path.join(self.idf_path, self.UT_BIN_FOLDER) - test_configs = os.listdir(output_folder) - for config in test_configs: - config_output_folder = os.path.join(output_folder, config) - if os.path.exists(config_output_folder): - test_cases.extend(self.parse_test_cases_for_one_config(config_output_folder, config)) - test_cases.sort(key=lambda x: x["config"] + x["summary"]) - self.dump_test_cases(test_cases) - - -def test_parser(): - parser = Parser() - # test parsing tags - # parsing module only and module in module list - prop = parser.parse_case_properities("[esp32]") - assert prop["module"] == "esp32" - # module not in module list - prop = parser.parse_case_properities("[not_in_list]") - assert prop["module"] == "misc" - # parsing a default tag, a tag with assigned value - prop = parser.parse_case_properities("[esp32][ignore][test_env=ABCD][not_support1][not_support2=ABCD]") - assert prop["ignore"] == "Yes" and prop["test_env"] == "ABCD" \ - and "not_support1" not in prop and "not_supported2" not in prop - # parsing omitted value - prop = parser.parse_case_properities("[esp32]") - assert prop["ignore"] == "No" and prop["test_env"] == "UT_T1_1" - # parsing with incorrect format - try: - parser.parse_case_properities("abcd") - assert False - except AssertionError: - pass - # skip invalid data parse, [type=] assigns empty string to type - prop = parser.parse_case_properities("[esp32]abdc aaaa [ignore=]") - assert prop["module"] == "esp32" and prop["ignore"] == "" - # skip mis-paired [] - prop = parser.parse_case_properities("[esp32][[ignore=b]][]][test_env=AAA]]") - assert prop["module"] == "esp32" and prop["ignore"] == "b" and prop["test_env"] == "AAA" - - config_dependency = { - 'a': '123', - 'b': '456', - 'c': 'not {123}', - 'd': '{123} and not {456}', - 'e': '{123} and not {789}', - 'f': '({123} and {456}) or ({123} and {789})' - } - sdkconfig = ["123", "789"] - tags = parser.parse_tags_internal(sdkconfig, config_dependency, parser.CONFIG_PATTERN) - assert sorted(tags) == ['a', 'd', 'f'] # sorted is required for older Python3, e.g. 3.4.8 - - -def main(): - test_parser() - - idf_path = os.getenv("IDF_PATH") - - parser = Parser(idf_path) - parser.parse_test_cases() - parser.copy_module_def_file() - if len(parser.parsing_errors) > 0: - for error in parser.parsing_errors: - print(error) - exit(-1) - - -if __name__ == '__main__': - main() diff --git a/tools/unit-test-app/unit_test.py b/tools/unit-test-app/unit_test.py deleted file mode 100755 index eb300564..00000000 --- a/tools/unit-test-app/unit_test.py +++ /dev/null @@ -1,748 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2018 Espressif Systems (Shanghai) PTE LTD -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Test script for unit test case. -""" - -import re -import os -import sys -import time -import argparse -import threading - -# if we want to run test case outside `tiny-test-fw` folder, -# we need to insert tiny-test-fw path into sys path -test_fw_path = os.getenv("TEST_FW_PATH") -if test_fw_path and test_fw_path not in sys.path: - sys.path.insert(0, test_fw_path) - -import TinyFW -import IDF -import Utility -import Env -from DUT import ExpectTimeout -from IDF.IDFApp import UT - - -UT_APP_BOOT_UP_DONE = "Press ENTER to see the list of tests." -RESET_PATTERN = re.compile(r"(ets [\w]{3}\s+[\d]{1,2} [\d]{4} [\d]{2}:[\d]{2}:[\d]{2}[^()]*\([\w].*?\))") -EXCEPTION_PATTERN = re.compile(r"(Guru Meditation Error: Core\s+\d panic'ed \([\w].*?\))") -ABORT_PATTERN = re.compile(r"(abort\(\) was called at PC 0x[a-fA-F\d]{8} on core \d)") -FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored") -END_LIST_STR = r'\r?\nEnter test for running' -TEST_PATTERN = re.compile(r'\((\d+)\)\s+"([^"]+)" ([^\r\n]+)\r?\n(' + END_LIST_STR + r')?') -TEST_SUBMENU_PATTERN = re.compile(r'\s+\((\d+)\)\s+"[^"]+"\r?\n(?=(?=\()|(' + END_LIST_STR + r'))') - -SIMPLE_TEST_ID = 0 -MULTI_STAGE_ID = 1 -MULTI_DEVICE_ID = 2 - -DEFAULT_TIMEOUT = 20 - -DUT_STARTUP_CHECK_RETRY_COUNT = 5 -TEST_HISTROY_CHECK_TIMEOUT = 1 - - -def format_test_case_config(test_case_data): - """ - convert the test case data to unified format. - We need to following info to run unit test cases: - - 1. unit test app config - 2. test case name - 3. test case reset info - - the formatted case config is a dict, with ut app config as keys. The value is a list of test cases. - Each test case is a dict with "name" and "reset" as keys. For example:: - - case_config = { - "default": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, {...}], - "psram": [{"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}], - } - - If config is not specified for test case, then - - :param test_case_data: string, list, or a dictionary list - :return: formatted data - """ - - case_config = dict() - - def parse_case(one_case_data): - """ parse and format one case """ - - def process_reset_list(reset_list): - # strip space and remove white space only items - _output = list() - for _r in reset_list: - _data = _r.strip(" ") - if _data: - _output.append(_data) - return _output - - _case = dict() - if isinstance(one_case_data, str): - _temp = one_case_data.split(" [reset=") - _case["name"] = _temp[0] - try: - _case["reset"] = process_reset_list(_temp[1][0:-1].split(",")) - except IndexError: - _case["reset"] = list() - elif isinstance(one_case_data, dict): - _case = one_case_data.copy() - assert "name" in _case - if "reset" not in _case: - _case["reset"] = list() - else: - if isinstance(_case["reset"], str): - _case["reset"] = process_reset_list(_case["reset"].split(",")) - else: - raise TypeError("Not supported type during parsing unit test case") - - if "config" not in _case: - _case["config"] = "default" - - return _case - - if not isinstance(test_case_data, list): - test_case_data = [test_case_data] - - for case_data in test_case_data: - parsed_case = parse_case(case_data) - try: - case_config[parsed_case["config"]].append(parsed_case) - except KeyError: - case_config[parsed_case["config"]] = [parsed_case] - - return case_config - - -def replace_app_bin(dut, name, new_app_bin): - if new_app_bin is None: - return - search_pattern = '/{}.bin'.format(name) - for i, config in enumerate(dut.download_config): - if config.endswith(search_pattern): - dut.download_config[i] = new_app_bin - Utility.console_log("The replaced application binary is {}".format(new_app_bin), "O") - break - - -def reset_dut(dut): - dut.reset() - # esptool ``run`` cmd takes quite long time. - # before reset finish, serial port is closed. therefore DUT could already bootup before serial port opened. - # this could cause checking bootup print failed. - # now use input cmd `-` and check test history to check if DUT is bootup. - # we'll retry this step for a few times, - # in case `dut.reset` returns during DUT bootup (when DUT can't process any command). - for _ in range(DUT_STARTUP_CHECK_RETRY_COUNT): - dut.write("-") - try: - dut.expect("0 Tests 0 Failures 0 Ignored", timeout=TEST_HISTROY_CHECK_TIMEOUT) - break - except ExpectTimeout: - pass - else: - raise AssertionError("Reset {} ({}) failed!".format(dut.name, dut.port)) - - -def run_one_normal_case(dut, one_case, junit_test_case, failed_cases): - - reset_dut(dut) - - dut.start_capture_raw_data() - # run test case - dut.write("\"{}\"".format(one_case["name"])) - dut.expect("Running " + one_case["name"] + "...") - - exception_reset_list = [] - - # we want to set this flag in callbacks (inner functions) - # use list here so we can use append to set this flag - test_finish = list() - - # expect callbacks - def one_case_finish(result): - """ one test finished, let expect loop break and log result """ - test_finish.append(True) - output = dut.stop_capture_raw_data() - if result: - Utility.console_log("Success: " + one_case["name"], color="green") - else: - failed_cases.append(one_case["name"]) - Utility.console_log("Failed: " + one_case["name"], color="red") - junit_test_case.add_failure_info(output) - - def handle_exception_reset(data): - """ - just append data to exception list. - exception list will be checked in ``handle_reset_finish``, once reset finished. - """ - exception_reset_list.append(data[0]) - - def handle_test_finish(data): - """ test finished without reset """ - # in this scenario reset should not happen - assert not exception_reset_list - if int(data[1]): - # case ignored - Utility.console_log("Ignored: " + one_case["name"], color="orange") - junit_test_case.add_skipped_info("ignored") - one_case_finish(not int(data[0])) - - def handle_reset_finish(data): - """ reset happened and reboot finished """ - assert exception_reset_list # reboot but no exception/reset logged. should never happen - result = False - if len(one_case["reset"]) == len(exception_reset_list): - for i, exception in enumerate(exception_reset_list): - if one_case["reset"][i] not in exception: - break - else: - result = True - if not result: - err_msg = "Reset Check Failed: \r\n\tExpected: {}\r\n\tGet: {}".format(one_case["reset"], - exception_reset_list) - Utility.console_log(err_msg, color="orange") - junit_test_case.add_error_info(err_msg) - one_case_finish(result) - - while not test_finish: - try: - dut.expect_any((RESET_PATTERN, handle_exception_reset), - (EXCEPTION_PATTERN, handle_exception_reset), - (ABORT_PATTERN, handle_exception_reset), - (FINISH_PATTERN, handle_test_finish), - (UT_APP_BOOT_UP_DONE, handle_reset_finish), - timeout=one_case["timeout"]) - except ExpectTimeout: - Utility.console_log("Timeout in expect", color="orange") - junit_test_case.add_error_info("timeout") - one_case_finish(False) - break - - -@IDF.idf_unit_test(env_tag="UT_T1_1", junit_report_by_case=True) -def run_unit_test_cases(env, extra_data): - """ - extra_data can be three types of value - 1. as string: - 1. "case_name" - 2. "case_name [reset=RESET_REASON]" - 2. as dict: - 1. with key like {"name": "Intr_alloc test, shared ints"} - 2. with key like {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET", "config": "psram"} - 3. as list of string or dict: - [case1, case2, case3, {"name": "restart from PRO CPU", "reset": "SW_CPU_RESET"}, ...] - - :param env: test env instance - :param extra_data: the case name or case list or case dictionary - :return: None - """ - - case_config = format_test_case_config(extra_data) - - # we don't want stop on failed case (unless some special scenarios we can't handle) - # this flag is used to log if any of the case failed during executing - # Before exit test function this flag is used to log if the case fails - failed_cases = [] - - for ut_config in case_config: - Utility.console_log("Running unit test for config: " + ut_config, "O") - dut = env.get_dut("unit-test-app", app_path=ut_config) - if len(case_config[ut_config]) > 0: - replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin')) - dut.start_app() - Utility.console_log("Download finished, start running test cases", "O") - - for one_case in case_config[ut_config]: - # create junit report test case - junit_test_case = TinyFW.JunitReport.create_test_case("[{}] {}".format(ut_config, one_case["name"])) - try: - run_one_normal_case(dut, one_case, junit_test_case, failed_cases) - TinyFW.JunitReport.test_case_finish(junit_test_case) - except Exception as e: - junit_test_case.add_error_info("Unexpected exception: " + str(e)) - TinyFW.JunitReport.test_case_finish(junit_test_case) - - # raise exception if any case fails - if failed_cases: - Utility.console_log("Failed Cases:", color="red") - for _case_name in failed_cases: - Utility.console_log("\t" + _case_name, color="red") - raise AssertionError("Unit Test Failed") - - -class Handler(threading.Thread): - - WAIT_SIGNAL_PATTERN = re.compile(r'Waiting for signal: \[(.+)\]!') - SEND_SIGNAL_PATTERN = re.compile(r'Send signal: \[(.+)\]!') - FINISH_PATTERN = re.compile(r"1 Tests (\d) Failures (\d) Ignored") - - def __init__(self, dut, sent_signal_list, lock, parent_case_name, child_case_index, timeout): - self.dut = dut - self.sent_signal_list = sent_signal_list - self.lock = lock - self.parent_case_name = parent_case_name - self.child_case_name = "" - self.child_case_index = child_case_index + 1 - self.finish = False - self.result = False - self.output = "" - self.fail_name = None - self.timeout = timeout - self.force_stop = threading.Event() # it show the running status - - reset_dut(self.dut) # reset the board to make it start from begining - - threading.Thread.__init__(self, name="{} Handler".format(dut)) - - def run(self): - - self.dut.start_capture_raw_data() - - def get_child_case_name(data): - self.child_case_name = data[0] - time.sleep(1) - self.dut.write(str(self.child_case_index)) - - def one_device_case_finish(result): - """ one test finished, let expect loop break and log result """ - self.finish = True - self.result = result - self.output = "[{}]\n\n{}\n".format(self.child_case_name, - self.dut.stop_capture_raw_data()) - if not result: - self.fail_name = self.child_case_name - - def device_wait_action(data): - start_time = time.time() - expected_signal = data[0] - while 1: - if time.time() > start_time + self.timeout: - Utility.console_log("Timeout in device for function: %s" % self.child_case_name, color="orange") - break - with self.lock: - if expected_signal in self.sent_signal_list: - self.dut.write(" ") - self.sent_signal_list.remove(expected_signal) - break - time.sleep(0.01) - - def device_send_action(data): - with self.lock: - self.sent_signal_list.append(data[0].encode('utf-8')) - - def handle_device_test_finish(data): - """ test finished without reset """ - # in this scenario reset should not happen - if int(data[1]): - # case ignored - Utility.console_log("Ignored: " + self.child_case_name, color="orange") - one_device_case_finish(not int(data[0])) - - try: - time.sleep(1) - self.dut.write("\"{}\"".format(self.parent_case_name)) - self.dut.expect("Running " + self.parent_case_name + "...") - except ExpectTimeout: - Utility.console_log("No case detected!", color="orange") - while not self.finish and not self.force_stop.isSet(): - try: - self.dut.expect_any((re.compile('\(' + str(self.child_case_index) + '\)\s"(\w+)"'), - get_child_case_name), - (self.WAIT_SIGNAL_PATTERN, device_wait_action), # wait signal pattern - (self.SEND_SIGNAL_PATTERN, device_send_action), # send signal pattern - (self.FINISH_PATTERN, handle_device_test_finish), # test finish pattern - timeout=self.timeout) - except ExpectTimeout: - Utility.console_log("Timeout in expect", color="orange") - one_device_case_finish(False) - break - - def stop(self): - self.force_stop.set() - - -def get_case_info(one_case): - parent_case = one_case["name"] - child_case_num = one_case["child case num"] - return parent_case, child_case_num - - -def get_dut(duts, env, name, ut_config, app_bin=None): - if name in duts: - dut = duts[name] - else: - dut = env.get_dut(name, app_path=ut_config) - duts[name] = dut - replace_app_bin(dut, "unit-test-app", app_bin) - dut.start_app() # download bin to board - return dut - - -def run_one_multiple_devices_case(duts, ut_config, env, one_case, failed_cases, app_bin, junit_test_case): - lock = threading.RLock() - threads = [] - send_signal_list = [] - result = True - parent_case, case_num = get_case_info(one_case) - - for i in range(case_num): - dut = get_dut(duts, env, "dut%d" % i, ut_config, app_bin) - threads.append(Handler(dut, send_signal_list, lock, - parent_case, i, one_case["timeout"])) - for thread in threads: - thread.setDaemon(True) - thread.start() - output = "Multiple Device Failed\n" - for thread in threads: - thread.join() - result = result and thread.result - output += thread.output - if not thread.result: - [thd.stop() for thd in threads] - - if result: - Utility.console_log("Success: " + one_case["name"], color="green") - else: - failed_cases.append(one_case["name"]) - junit_test_case.add_failure_info(output) - Utility.console_log("Failed: " + one_case["name"], color="red") - - -@IDF.idf_unit_test(env_tag="UT_T2_1", junit_report_by_case=True) -def run_multiple_devices_cases(env, extra_data): - """ - extra_data can be two types of value - 1. as dict: - e.g. - {"name": "gpio master/slave test example", - "child case num": 2, - "config": "release", - "env_tag": "UT_T2_1"} - 2. as list dict: - e.g. - [{"name": "gpio master/slave test example1", - "child case num": 2, - "config": "release", - "env_tag": "UT_T2_1"}, - {"name": "gpio master/slave test example2", - "child case num": 2, - "config": "release", - "env_tag": "UT_T2_1"}] - - """ - failed_cases = [] - case_config = format_test_case_config(extra_data) - duts = {} - for ut_config in case_config: - Utility.console_log("Running unit test for config: " + ut_config, "O") - for one_case in case_config[ut_config]: - junit_test_case = TinyFW.JunitReport.create_test_case("[{}] {}".format(ut_config, one_case["name"])) - try: - run_one_multiple_devices_case(duts, ut_config, env, one_case, failed_cases, - one_case.get('app_bin'), junit_test_case) - TinyFW.JunitReport.test_case_finish(junit_test_case) - except Exception as e: - junit_test_case.add_error_info("Unexpected exception: " + str(e)) - TinyFW.JunitReport.test_case_finish(junit_test_case) - - if failed_cases: - Utility.console_log("Failed Cases:", color="red") - for _case_name in failed_cases: - Utility.console_log("\t" + _case_name, color="red") - raise AssertionError("Unit Test Failed") - - -def run_one_multiple_stage_case(dut, one_case, failed_cases, junit_test_case): - reset_dut(dut) - - dut.start_capture_raw_data() - - exception_reset_list = [] - - for test_stage in range(one_case["child case num"]): - # select multi stage test case name - dut.write("\"{}\"".format(one_case["name"])) - dut.expect("Running " + one_case["name"] + "...") - # select test function for current stage - dut.write(str(test_stage + 1)) - - # we want to set this flag in callbacks (inner functions) - # use list here so we can use append to set this flag - stage_finish = list() - - def last_stage(): - return test_stage == one_case["child case num"] - 1 - - def check_reset(): - if one_case["reset"]: - assert exception_reset_list # reboot but no exception/reset logged. should never happen - result = False - if len(one_case["reset"]) == len(exception_reset_list): - for i, exception in enumerate(exception_reset_list): - if one_case["reset"][i] not in exception: - break - else: - result = True - if not result: - err_msg = "Reset Check Failed: \r\n\tExpected: {}\r\n\tGet: {}".format(one_case["reset"], - exception_reset_list) - Utility.console_log(err_msg, color="orange") - junit_test_case.add_error_info(err_msg) - else: - # we allow omit reset in multi stage cases - result = True - return result - - # expect callbacks - def one_case_finish(result): - """ one test finished, let expect loop break and log result """ - # handle test finish - result = result and check_reset() - output = dut.stop_capture_raw_data() - if result: - Utility.console_log("Success: " + one_case["name"], color="green") - else: - failed_cases.append(one_case["name"]) - Utility.console_log("Failed: " + one_case["name"], color="red") - junit_test_case.add_failure_info(output) - stage_finish.append("break") - - def handle_exception_reset(data): - """ - just append data to exception list. - exception list will be checked in ``handle_reset_finish``, once reset finished. - """ - exception_reset_list.append(data[0]) - - def handle_test_finish(data): - """ test finished without reset """ - # in this scenario reset should not happen - if int(data[1]): - # case ignored - Utility.console_log("Ignored: " + one_case["name"], color="orange") - junit_test_case.add_skipped_info("ignored") - # only passed in last stage will be regarded as real pass - if last_stage(): - one_case_finish(not int(data[0])) - else: - Utility.console_log("test finished before enter last stage", color="orange") - one_case_finish(False) - - def handle_next_stage(data): - """ reboot finished. we goto next stage """ - if last_stage(): - # already last stage, should never goto next stage - Utility.console_log("didn't finish at last stage", color="orange") - one_case_finish(False) - else: - stage_finish.append("continue") - - while not stage_finish: - try: - dut.expect_any((RESET_PATTERN, handle_exception_reset), - (EXCEPTION_PATTERN, handle_exception_reset), - (ABORT_PATTERN, handle_exception_reset), - (FINISH_PATTERN, handle_test_finish), - (UT_APP_BOOT_UP_DONE, handle_next_stage), - timeout=one_case["timeout"]) - except ExpectTimeout: - Utility.console_log("Timeout in expect", color="orange") - one_case_finish(False) - break - if stage_finish[0] == "break": - # test breaks on current stage - break - - -@IDF.idf_unit_test(env_tag="UT_T1_1", junit_report_by_case=True) -def run_multiple_stage_cases(env, extra_data): - """ - extra_data can be 2 types of value - 1. as dict: Mandantory keys: "name" and "child case num", optional keys: "reset" and others - 3. as list of string or dict: - [case1, case2, case3, {"name": "restart from PRO CPU", "child case num": 2}, ...] - - :param env: test env instance - :param extra_data: the case name or case list or case dictionary - :return: None - """ - - case_config = format_test_case_config(extra_data) - - # we don't want stop on failed case (unless some special scenarios we can't handle) - # this flag is used to log if any of the case failed during executing - # Before exit test function this flag is used to log if the case fails - failed_cases = [] - - for ut_config in case_config: - Utility.console_log("Running unit test for config: " + ut_config, "O") - dut = env.get_dut("unit-test-app", app_path=ut_config) - if len(case_config[ut_config]) > 0: - replace_app_bin(dut, "unit-test-app", case_config[ut_config][0].get('app_bin')) - dut.start_app() - - for one_case in case_config[ut_config]: - junit_test_case = TinyFW.JunitReport.create_test_case("[{}] {}".format(ut_config, one_case["name"])) - try: - run_one_multiple_stage_case(dut, one_case, failed_cases, junit_test_case) - TinyFW.JunitReport.test_case_finish(junit_test_case) - except Exception as e: - junit_test_case.add_error_info("Unexpected exception: " + str(e)) - TinyFW.JunitReport.test_case_finish(junit_test_case) - - # raise exception if any case fails - if failed_cases: - Utility.console_log("Failed Cases:", color="red") - for _case_name in failed_cases: - Utility.console_log("\t" + _case_name, color="red") - raise AssertionError("Unit Test Failed") - - -def detect_update_unit_test_info(env, extra_data, app_bin): - - case_config = format_test_case_config(extra_data) - - for ut_config in case_config: - dut = env.get_dut("unit-test-app", app_path=ut_config) - replace_app_bin(dut, "unit-test-app", app_bin) - dut.start_app() - - reset_dut(dut) - - # get the list of test cases - dut.write("") - dut.expect("Here's the test menu, pick your combo:", timeout=DEFAULT_TIMEOUT) - - def find_update_dic(name, _t, _timeout, child_case_num=None): - for _case_data in extra_data: - if _case_data['name'] == name: - _case_data['type'] = _t - if 'timeout' not in _case_data: - _case_data['timeout'] = _timeout - if child_case_num: - _case_data['child case num'] = child_case_num - - try: - while True: - data = dut.expect(TEST_PATTERN, timeout=DEFAULT_TIMEOUT) - test_case_name = data[1] - m = re.search(r'\[timeout=(\d+)\]', data[2]) - if m: - timeout = int(m.group(1)) - else: - timeout = 30 - m = re.search(r'\[multi_stage\]', data[2]) - if m: - test_case_type = MULTI_STAGE_ID - else: - m = re.search(r'\[multi_device\]', data[2]) - if m: - test_case_type = MULTI_DEVICE_ID - else: - test_case_type = SIMPLE_TEST_ID - find_update_dic(test_case_name, test_case_type, timeout) - if data[3] and re.search(END_LIST_STR, data[3]): - break - continue - # find the last submenu item - data = dut.expect(TEST_SUBMENU_PATTERN, timeout=DEFAULT_TIMEOUT) - find_update_dic(test_case_name, test_case_type, timeout, child_case_num=int(data[0])) - if data[1] and re.search(END_LIST_STR, data[1]): - break - # check if the unit test case names are correct, i.e. they could be found in the device - for _dic in extra_data: - if 'type' not in _dic: - raise ValueError("Unit test \"{}\" doesn't exist in the flashed device!".format(_dic.get('name'))) - except ExpectTimeout: - Utility.console_log("Timeout during getting the test list", color="red") - finally: - dut.close() - - # These options are the same for all configs, therefore there is no need to continue - break - - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument( - '--repeat', '-r', - help='Number of repetitions for the test(s). Default is 1.', - type=int, - default=1 - ) - parser.add_argument("--env_config_file", "-e", - help="test env config file", - default=None - ) - parser.add_argument("--app_bin", "-b", - help="application binary file for flashing the chip", - default=None - ) - parser.add_argument( - 'test', - help='Comma separated list of