mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-22 01:27:11 +08:00
Merge branch 'feature/update_unit_test_app' into 'master'
Update unit-test-app See merge request sdk/ESP8266_RTOS_SDK!845
This commit is contained in:
@ -1,4 +0,0 @@
|
||||
#
|
||||
#Component Makefile
|
||||
#
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Tests for bootloader_support esp_load(ESP_IMAGE_VERIFY, ...)
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
|
||||
}
|
25
components/esp8266/include/esp_clk.h
Normal file
25
components/esp8266/include/esp_clk.h
Normal file
@ -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);
|
@ -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;
|
||||
|
||||
|
127
components/esp8266/include/rom/uart.h
Normal file
127
components/esp8266/include/rom/uart.h
Normal file
@ -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_ */
|
40
components/esp8266/include/soc/cpu.h
Normal file
40
components/esp8266/include/soc/cpu.h
Normal file
@ -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 <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#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
|
@ -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 );
|
||||
PROVIDE ( ets_io_vprintf = 0x40001f00 );
|
||||
PROVIDE ( uart_rx_one_char = 0x40003b8c );
|
@ -1,6 +0,0 @@
|
||||
set(COMPONENT_SRCDIRS ".")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
set(COMPONENT_REQUIRES unity esp_http_server)
|
||||
|
||||
register_component()
|
@ -1 +0,0 @@
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
@ -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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_http_server.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -148,6 +148,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.
|
||||
*/
|
||||
|
@ -1,5 +0,0 @@
|
||||
#
|
||||
#Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
@ -1,97 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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);
|
||||
}
|
@ -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()
|
@ -1,2 +0,0 @@
|
||||
COMPONENT_PRIV_INCLUDEDIRS := ../proto-c/
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
File diff suppressed because it is too large
Load Diff
6
tools/unit-test-app/CMakeLists.txt
Normal file
6
tools/unit-test-app/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(unit-test-app)
|
@ -5,9 +5,11 @@
|
||||
|
||||
PROJECT_NAME := unit-test-app
|
||||
|
||||
NON_INTERACTIVE_TARGET += ut-apply-config-% ut-clean-%
|
||||
ifeq ($(MAKELEVEL),0)
|
||||
# Set default target
|
||||
all:
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
# Define helper targets only when not recursing
|
||||
|
||||
# List of unit-test-app configurations.
|
||||
# Each file in configs/ directory defines a configuration. The format is the
|
||||
@ -21,8 +23,9 @@ CONFIG_CLEAN_TARGETS := $(addprefix ut-clean-,$(CONFIG_NAMES))
|
||||
CONFIG_APPLY_TARGETS := $(addprefix ut-apply-config-,$(CONFIG_NAMES))
|
||||
|
||||
# Build (intermediate) and output (artifact) directories
|
||||
BUILDS_DIR := $(PROJECT_PATH)/builds
|
||||
BINARIES_DIR := $(PROJECT_PATH)/output
|
||||
PROJECT_DIR := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
|
||||
BUILDS_DIR := $(PROJECT_DIR)/builds
|
||||
BINARIES_DIR := $(PROJECT_DIR)/output
|
||||
|
||||
# This generates per-config targets (clean, build, apply-config).
|
||||
define GenerateConfigTargets
|
||||
@ -56,18 +59,30 @@ $(BINARIES_DIR)/%/$(PROJECT_NAME).bin: configs/%
|
||||
mkdir -p $(BINARIES_DIR)/$*/bootloader
|
||||
mkdir -p $(BUILDS_DIR)/$*
|
||||
# Prepare configuration: top-level sdkconfig.defaults file plus the current configuration (configs/$*)
|
||||
$(summary) CONFIG $(BUILDS_DIR)/$*/sdkconfig
|
||||
echo CONFIG $(BUILDS_DIR)/$*/sdkconfig
|
||||
rm -f $(BUILDS_DIR)/$*/sdkconfig
|
||||
cat sdkconfig.defaults > $(BUILDS_DIR)/$*/sdkconfig.defaults
|
||||
echo "" >> $(BUILDS_DIR)/$*/sdkconfig.defaults # in case there is no trailing newline in sdkconfig.defaults
|
||||
cat configs/$* >> $(BUILDS_DIR)/$*/sdkconfig.defaults
|
||||
|
||||
# Build, tweaking paths to sdkconfig and sdkconfig.defaults
|
||||
$(summary) BUILD_CONFIG $(BUILDS_DIR)/$*
|
||||
$(MAKE) defconfig all \
|
||||
echo BUILD_CONFIG $(BUILDS_DIR)/$*
|
||||
# 'TEST_COMPONENTS=names' option can be added to configs/$* to limit the set
|
||||
# of tests to build for given configuration.
|
||||
# Build all tests if this option is not present.
|
||||
test_components=`sed -n 's/^TEST_COMPONENTS=\(.*\)/\1/p' configs/$*`; \
|
||||
test_exclude_components=`sed -n 's/^TEST_EXCLUDE_COMPONENTS=\(.*\)/\1/p' configs/$*`; \
|
||||
tests_all=`test -n "$${test_components}"; echo $${?}`; \
|
||||
exclude_components=`sed -n 's/^EXCLUDE_COMPONENTS=\(.*\)/\1/p' configs/$*`; \
|
||||
$(MAKE) defconfig list-components all \
|
||||
BUILD_DIR_BASE=$(BUILDS_DIR)/$* \
|
||||
SDKCONFIG=$(BUILDS_DIR)/$*/sdkconfig \
|
||||
SDKCONFIG_DEFAULTS=$(BUILDS_DIR)/$*/sdkconfig.defaults
|
||||
$(MAKE) print_flash_cmd \
|
||||
SDKCONFIG_DEFAULTS=$(BUILDS_DIR)/$*/sdkconfig.defaults \
|
||||
TEST_COMPONENTS="$${test_components}" \
|
||||
TEST_EXCLUDE_COMPONENTS="$${test_exclude_components}" \
|
||||
TESTS_ALL=$${tests_all} \
|
||||
EXCLUDE_COMPONENTS="$${exclude_components}"
|
||||
$(MAKE) --silent print_flash_cmd \
|
||||
BUILD_DIR_BASE=$(BUILDS_DIR)/$* \
|
||||
SDKCONFIG=$(BUILDS_DIR)/$*/sdkconfig \
|
||||
| sed -e 's:'$(BUILDS_DIR)/$*/'::g' \
|
||||
@ -77,7 +92,7 @@ $(BINARIES_DIR)/%/$(PROJECT_NAME).bin: configs/%
|
||||
cp $(BUILDS_DIR)/$*/$(PROJECT_NAME).elf $(BINARIES_DIR)/$*/
|
||||
cp $(BUILDS_DIR)/$*/$(PROJECT_NAME).bin $(BINARIES_DIR)/$*/
|
||||
cp $(BUILDS_DIR)/$*/$(PROJECT_NAME).map $(BINARIES_DIR)/$*/
|
||||
cp $(BUILDS_DIR)/$*/partition_table*.bin $(BINARIES_DIR)/$*/
|
||||
cp $(BUILDS_DIR)/$*/*.bin $(BINARIES_DIR)/$*/
|
||||
cp $(BUILDS_DIR)/$*/sdkconfig $(BINARIES_DIR)/$*/
|
||||
|
||||
|
||||
@ -87,17 +102,44 @@ ut-help:
|
||||
@echo "make ut-build-NAME - Build unit-test-app with configuration provided in configs/NAME."
|
||||
@echo " Build directory will be builds/NAME/, output binaries will be"
|
||||
@echo " under output/NAME/"
|
||||
@echo "make ut-clean-NAME - Remove build and output directories for configuration NAME."
|
||||
@echo ""
|
||||
@echo "make ut-build-all-configs - Build all configurations defined in configs/ directory."
|
||||
@echo ""
|
||||
@echo "Above targets determine list of components to be built from configs/NAME files."
|
||||
@echo "To build custom subset of components use 'make ut-apply-config-NAME' and then 'make all'."
|
||||
@echo ""
|
||||
@echo "make ut-apply-config-NAME - Generates configuration based on configs/NAME in sdkconfig"
|
||||
@echo " file. After this, normal all/flash targets can be used."
|
||||
@echo " Useful for development/debugging."
|
||||
@echo ""
|
||||
@echo "make ut-clean-NAME - Remove build and output directories for configuration NAME."
|
||||
@echo ""
|
||||
|
||||
help: ut-help
|
||||
|
||||
.PHONY: ut-build-all-configs ut-clean-all-configs \
|
||||
$(CONFIG_BUILD_TARGETS) $(CONFIG_CLEAN_TARGETS) $(CONFIG_APPLY_TARGETS) \
|
||||
LOCAL_TARGETS := ut-build-all-configs ut-clean-all-configs \
|
||||
$(CONFIG_BUILD_TARGETS) $(CONFIG_CLEAN_TARGETS) \
|
||||
ut-help
|
||||
|
||||
.PHONY: $(LOCAL_TARGETS)
|
||||
|
||||
NON_INTERACTIVE_TARGET += ut-apply-config-% ut-clean-% ut-build-% \
|
||||
ut-build-all-configs ut-clean-all-configs
|
||||
|
||||
endif # MAKELEVEL == 0
|
||||
|
||||
|
||||
# When targets defined in this makefile are built, don't need to include the main project makefile.
|
||||
# This prevents some variables which depend on build directory from being set erroneously.
|
||||
ifeq ($(filter $(LOCAL_TARGETS),$(MAKECMDGOALS)),)
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
endif
|
||||
|
||||
# If recursing, print the actual list of tests being built
|
||||
ifneq ($(MAKELEVEL),0)
|
||||
|
||||
$(info TESTS $(foreach comp,$(TEST_COMPONENT_NAMES),$(patsubst %_test,%,$(comp))))
|
||||
|
||||
endif # MAKELEVEL != 0
|
||||
|
@ -4,6 +4,8 @@ ESP-IDF unit tests are run using Unit Test App. The app can be built with the un
|
||||
|
||||
# Building Unit Test App
|
||||
|
||||
## GNU Make
|
||||
|
||||
* Follow the setup instructions in the top-level esp-idf README.
|
||||
* Set IDF_PATH environment variable to point to the path to the esp-idf top-level directory.
|
||||
* Change into `tools/unit-test-app` directory
|
||||
@ -12,11 +14,21 @@ ESP-IDF unit tests are run using Unit Test App. The app can be built with the un
|
||||
* Follow the printed instructions to flash, or run `make flash`.
|
||||
* Unit test have a few preset sdkconfigs. It provides command `make ut-clean-config_name` and `make ut-build-config_name` (where `config_name` is the file name under `unit-test-app/configs` folder) to build with preset configs. For example, you can use `make ut-build-default TESTS_ALL=1` to build with config file `unit-test-app/configs/default`. Built binary for this config will be copied to `unit-test-app/output/config_name` folder.
|
||||
|
||||
## CMake
|
||||
|
||||
* Follow the setup instructions in the top-level esp-idf README.
|
||||
* Set IDF_PATH environment variable to point to the path to the esp-idf top-level directory.
|
||||
* Change into `tools/unit-test-app` directory
|
||||
* `idf.py menuconfig` to configure the Unit Test App.
|
||||
* `idf.py build -T <component> <component> ...` with `component` set to names of the components to be included in the test app. Or `idf.py build -T all` to build the test app with all the tests for components having `test` subdirectory.
|
||||
* Follow the printed instructions to flash, or run `idf.py flash -p PORT`.
|
||||
* Unit test have a few preset sdkconfigs. It provides command `idf.py ut-clean-config_name` and `idf.py ut-build-config_name` (where `config_name` is the file name under `unit-test-app/configs` folder) to build with preset configs. For example, you can use `idf.py ut-build-default -T all` to build with config file `unit-test-app/configs/default`. Built binary for this config will be copied to `unit-test-app/output/config_name` folder.
|
||||
|
||||
# Flash Size
|
||||
|
||||
The unit test partition table assumes a 4MB flash size. When testing `TESTS_ALL=1`, this additional factory app partition size is required.
|
||||
The unit test partition table assumes a 4MB flash size. When testing `TESTS_ALL=1` (Make) or `-T all` (CMake), this additional factory app partition size is required.
|
||||
|
||||
If building unit tests to run on a smaller flash size, edit `partition_table_unit_tests_app.csv` and use `TEST_COMPONENTS=` instead of `TESTS_ALL` if tests don't fit in a smaller factory app partition (exact size will depend on configured options).
|
||||
If building unit tests to run on a smaller flash size, edit `partition_table_unit_tests_app.csv` and use `TEST_COMPONENTS=` (Make) or `-T <component> <component> ...` (CMake) instead of `TESTS_ALL` or `-T all` if tests don't fit in a smaller factory app partition (exact size will depend on configured options).
|
||||
|
||||
# Running Unit Tests
|
||||
|
||||
@ -68,7 +80,7 @@ Unit test jobs will do reset before running each case (because some cases do not
|
||||
Gitlab CI do not support create jobs at runtime. We must maunally add all jobs to CI config file. To make test running in parallel, we limit the number of cases running on each job. When add new unit test cases, it could exceed the limitation that current unit test jobs support. In this case, assign test job will raise error, remind you to add jobs to `.gitlab-ci.yml`.
|
||||
|
||||
```
|
||||
Please add the following jobs to .gitlab-ci.yml with specific tags:
|
||||
Too many test cases vs jobs to run. Please add the following jobs to .gitlab-ci.yml with specific tags:
|
||||
* Add job with: UT_T1_1, ESP32_IDF, psram
|
||||
* Add job with: UT_T1_1, ESP32_IDF
|
||||
```
|
||||
@ -103,9 +115,31 @@ If you want to reproduce locally, you need to:
|
||||
2. Check the following print in CI job to get the config name: `Running unit test for config: config_name`. Then flash the binary of this config to your board.
|
||||
3. Run the failed case on your board (refer to Running Unit Tests section).
|
||||
* There're some special UT cases (multiple stages case, multiple devices cases) which requires user interaction:
|
||||
* You can refer to [unit test document](https://esp-idf.readthedocs.io/en/latest/api-guides/unit-tests.html#running-unit-tests) to run test manually.
|
||||
* You can refer to [unit test document](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/unit-tests.html#running-unit-tests) to run test manually.
|
||||
* Or, you can use `tools/unit-test-app/unit_test.py` to run the test cases:
|
||||
* read document of tiny-test-fw, set correct `TEST_FW_PATH` and `IDF_PATH`
|
||||
* modify `unit_test.py`, pass the test cases need to test as parameter (refer to test function doc string for supported parameter format) to test functions.
|
||||
* use `python unit_test.py` to run test
|
||||
* run `unit_test.py` (see examples below)
|
||||
* You can also use `tools/tiny-test-fw/Runner.py` to run test cases (it will be the same as what Runner do). Please use `python Runner.py -c $CONFIG_FILE $IDF_PATH/tools/unit-test-app` command, where `CONFIG_FILE` is a YAML file with same name with CI job in `components/idf_test/unit_test/CIConfigs` (artifacts, need to be download from `assign_test` job).
|
||||
|
||||
## Running unit tests on local machine by `unit_test.py`
|
||||
|
||||
A couple of examples follow for running unit tests on local machine.
|
||||
|
||||
```bash
|
||||
# run a simple unit test
|
||||
./unit_test.py "UART can do select()"
|
||||
# repeat the tests two times
|
||||
./unit_test.py -r 2 "UART can do select()"
|
||||
# use custom environment config file
|
||||
./unit_test.py -e /tmp/EnvConfigTemplate.yml "UART can do select()"
|
||||
# use custom application binary
|
||||
./unit_test.py -b /tmp/app.bin "UART can do select()"
|
||||
# run a list of unit tests
|
||||
./unit_test.py "UART can do select()" "concurent selects work"
|
||||
# add some options for unit tests
|
||||
./unit_test.py "UART can do select()",timeout:10 "concurent selects work",config:release,env_tag:UT_T2_1
|
||||
# run a multi stage test (type of test and child case numbers are autodetected)
|
||||
./unit_test.py "check a time after wakeup from deep sleep"
|
||||
# run a list of different unit tests (one simple and one multi stage test)
|
||||
./unit_test.py "concurent selects work" "NOINIT attributes behavior"
|
||||
```
|
||||
|
10
tools/unit-test-app/components/unity/CMakeLists.txt
Normal file
10
tools/unit-test-app/components/unity/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(COMPONENT_SRCDIRS .)
|
||||
set(COMPONENT_ADD_INCLUDEDIRS include)
|
||||
|
||||
set(COMPONENT_REQUIRES spi_flash idf_test)
|
||||
|
||||
register_component()
|
||||
|
||||
if(GCC_NOT_5_2_0)
|
||||
component_compile_options(-Wno-unused-const-variable)
|
||||
endif()
|
15
tools/unit-test-app/components/unity/Kconfig
Normal file
15
tools/unit-test-app/components/unity/Kconfig
Normal file
@ -0,0 +1,15 @@
|
||||
menu "Unity test framework"
|
||||
|
||||
config UNITY_FREERTOS_PRIORITY
|
||||
int "Priority of Unity test task"
|
||||
default 5
|
||||
|
||||
config UNITY_FREERTOS_CPU
|
||||
int "CPU to run Unity test task on"
|
||||
default 0
|
||||
|
||||
config UNITY_FREERTOS_STACK_SIZE
|
||||
int "Stack size of Unity test task, in bytes"
|
||||
default 8192
|
||||
|
||||
endmenu
|
@ -1,3 +1,7 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
|
||||
ifeq ($(GCC_NOT_5_2_0), 1)
|
||||
unity.o: CFLAGS += -Wno-unused-const-variable
|
||||
endif
|
@ -0,0 +1,32 @@
|
||||
|
||||
/* @brief macro to print IDF performance
|
||||
* @param mode : performance item name. a string pointer.
|
||||
* @param value_fmt: print format and unit of the value, for example: "%02fms", "%dKB"
|
||||
* @param value : the performance value.
|
||||
*/
|
||||
#define IDF_LOG_PERFORMANCE(item, value_fmt, value) \
|
||||
printf("[Performance][%s]: "value_fmt"\n", item, value)
|
||||
|
||||
|
||||
/* declare the performance here */
|
||||
#define IDF_PERFORMANCE_MAX_HTTPS_REQUEST_BIN_SIZE 800
|
||||
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP 200
|
||||
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_PSRAM 300
|
||||
#define IDF_PERFORMANCE_MAX_FREERTOS_SPINLOCK_CYCLES_PER_OP_UNICORE 130
|
||||
#define IDF_PERFORMANCE_MAX_ESP_TIMER_GET_TIME_PER_CALL 1000
|
||||
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 30
|
||||
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 27
|
||||
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 15
|
||||
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 15
|
||||
/* Due to code size & linker layout differences interacting with cache, VFS
|
||||
microbenchmark currently runs slower with PSRAM enabled. */
|
||||
#define IDF_PERFORMANCE_MAX_VFS_OPEN_WRITE_CLOSE_TIME 50000
|
||||
#define IDF_PERFORMANCE_MAX_VFS_OPEN_WRITE_CLOSE_TIME_PSRAM 40000
|
||||
// throughput performance by iperf
|
||||
#define IDF_PERFORMANCE_MIN_TCP_RX_THROUGHPUT 50
|
||||
#define IDF_PERFORMANCE_MIN_TCP_TX_THROUGHPUT 40
|
||||
#define IDF_PERFORMANCE_MIN_UDP_RX_THROUGHPUT 80
|
||||
#define IDF_PERFORMANCE_MIN_UDP_TX_THROUGHPUT 50
|
||||
// events dispatched per second by event loop library
|
||||
#define IDF_PERFORMANCE_MIN_EVENT_DISPATCH 25000
|
||||
#define IDF_PERFORMANCE_MIN_EVENT_DISPATCH_PSRAM 21000
|
109
tools/unit-test-app/components/unity/include/test_utils.h
Normal file
109
tools/unit-test-app/components/unity/include/test_utils.h
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2015-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.
|
||||
#pragma once
|
||||
|
||||
// Utilities for esp-idf unit tests
|
||||
|
||||
#include <stdint.h>
|
||||
#include <esp_partition.h>
|
||||
|
||||
/* Return the 'flash_test' custom data partition (type 0x55)
|
||||
defined in the custom partition table.
|
||||
*/
|
||||
const esp_partition_t *get_test_data_partition();
|
||||
|
||||
/**
|
||||
* @brief Initialize reference clock
|
||||
*
|
||||
* Reference clock provides timestamps at constant 1 MHz frequency, even when
|
||||
* the APB frequency is changing.
|
||||
*/
|
||||
void ref_clock_init();
|
||||
|
||||
/**
|
||||
* @brief Deinitialize reference clock
|
||||
*/
|
||||
void ref_clock_deinit();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get reference clock timestamp
|
||||
* @return number of microseconds since the reference clock was initialized
|
||||
*/
|
||||
uint64_t ref_clock_get();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reset automatic leak checking which happens in unit tests.
|
||||
*
|
||||
* Updates recorded "before" free memory values to the free memory values
|
||||
* at time of calling. Resets leak checker if tracing is enabled in
|
||||
* config.
|
||||
*
|
||||
* This can be called if a test case does something which allocates
|
||||
* memory on first use, for example.
|
||||
*
|
||||
* @note Use with care as this can mask real memory leak problems.
|
||||
*/
|
||||
void unity_reset_leak_checks(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Call this function from a test case which requires TCP/IP or
|
||||
* LWIP functionality.
|
||||
*
|
||||
* @note This should be the first function the test case calls, as it will
|
||||
* allocate memory on first use (and also reset the test case leak checker).
|
||||
*/
|
||||
void test_case_uses_tcpip(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief wait for signals.
|
||||
*
|
||||
* for multiple devices test cases, DUT might need to wait for other DUTs before continue testing.
|
||||
* As all DUTs are independent, need user (or test script) interaction to make test synchronized.
|
||||
*
|
||||
* Here we provide signal functions for this.
|
||||
* For example, we're testing GPIO, DUT1 has one pin connect to with DUT2.
|
||||
* DUT2 will output high level and then DUT1 will read input.
|
||||
* DUT1 should call `unity_wait_for_signal("output high level");` before it reads input.
|
||||
* DUT2 should call `unity_send_signal("output high level");` after it finished setting output high level.
|
||||
* According to the console logs:
|
||||
*
|
||||
* DUT1 console:
|
||||
*
|
||||
* ```
|
||||
* Waiting for signal: [output high level]!
|
||||
* Please press "Enter" key to once any board send this signal.
|
||||
* ```
|
||||
*
|
||||
* DUT2 console:
|
||||
*
|
||||
* ```
|
||||
* Send signal: [output high level]!
|
||||
* ```
|
||||
*
|
||||
* Then we press Enter key on DUT1's console, DUT1 starts to read input and then test success.
|
||||
*
|
||||
* @param signal_name signal name which DUT expected to wait before proceed testing
|
||||
*/
|
||||
void unity_wait_for_signal(const char* signal_name);
|
||||
|
||||
/**
|
||||
* @brief DUT send signal.
|
||||
*
|
||||
* @param signal_name signal name which DUT send once it finished preparing.
|
||||
*/
|
||||
void unity_send_signal(const char* signal_name);
|
@ -16,6 +16,9 @@ extern "C"
|
||||
#define UNITY_INCLUDE_CONFIG_H
|
||||
#include "unity_internals.h"
|
||||
|
||||
/* include performance pass standards header file */
|
||||
#include "idf_performance.h"
|
||||
|
||||
void setUp(void);
|
||||
void tearDown(void);
|
||||
|
||||
|
@ -1,17 +1,19 @@
|
||||
#ifndef UNITY_CONFIG_H
|
||||
#define UNITY_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// This file gets included from unity.h via unity_internals.h
|
||||
// It is inside #ifdef __cplusplus / extern "C" block, so we can
|
||||
// only use C features here
|
||||
|
||||
// Adapt Unity to our environment, disable FP support
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
/* Some definitions applicable to Unity running in FreeRTOS */
|
||||
#define UNITY_FREERTOS_PRIORITY 5
|
||||
#define UNITY_FREERTOS_CPU 0
|
||||
#define UNITY_FREERTOS_PRIORITY CONFIG_UNITY_FREERTOS_PRIORITY
|
||||
#define UNITY_FREERTOS_CPU CONFIG_UNITY_FREERTOS_CPU
|
||||
#define UNITY_FREERTOS_STACK_SIZE CONFIG_UNITY_FREERTOS_STACK_SIZE
|
||||
|
||||
#define UNITY_EXCLUDE_FLOAT
|
||||
#define UNITY_EXCLUDE_DOUBLE
|
||||
@ -49,7 +51,7 @@
|
||||
|
||||
#define UNITY_TEST_FN_SET(...) \
|
||||
static test_func UNITY_TEST_UID(test_functions)[] = {__VA_ARGS__}; \
|
||||
static char* UNITY_TEST_UID(test_fn_name)[] = FN_NAME_SET(PP_NARG(__VA_ARGS__), __VA_ARGS__)
|
||||
static const char* UNITY_TEST_UID(test_fn_name)[] = FN_NAME_SET(PP_NARG(__VA_ARGS__), __VA_ARGS__)
|
||||
|
||||
|
||||
typedef void (* test_func)(void);
|
||||
@ -62,7 +64,7 @@ struct test_desc_t
|
||||
const char* file;
|
||||
int line;
|
||||
uint8_t test_fn_count;
|
||||
char ** test_fn_name;
|
||||
const char ** test_fn_name;
|
||||
struct test_desc_t* next;
|
||||
};
|
||||
|
||||
|
86
tools/unit-test-app/components/unity/test_utils.c
Normal file
86
tools/unit-test-app/components/unity/test_utils.c
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2015-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.
|
||||
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/uart.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "tcpip_adapter.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
const esp_partition_t *get_test_data_partition()
|
||||
{
|
||||
/* This finds "flash_test" partition defined in partition_table_unit_test_app.csv */
|
||||
const esp_partition_t *result = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_ANY, "flash_test");
|
||||
TEST_ASSERT_NOT_NULL(result); /* means partition table set wrong */
|
||||
return result;
|
||||
}
|
||||
|
||||
// wait user to send "Enter" key
|
||||
static void wait_user_control()
|
||||
{
|
||||
char sign[5] = {0};
|
||||
while(strlen(sign) == 0)
|
||||
{
|
||||
/* Flush anything already in the RX buffer */
|
||||
while(uart_rx_one_char((uint8_t *) sign) == OK) {
|
||||
}
|
||||
/* Read line */
|
||||
UartRxString((uint8_t*) sign, sizeof(sign) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void test_case_uses_tcpip()
|
||||
{
|
||||
// Can be called more than once, does nothing on subsequent calls
|
||||
tcpip_adapter_init();
|
||||
|
||||
// Allocate all sockets then free them
|
||||
// (First time each socket is allocated some one-time allocations happen.)
|
||||
int sockets[CONFIG_LWIP_MAX_SOCKETS];
|
||||
for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) {
|
||||
int type = (i % 2 == 0) ? SOCK_DGRAM : SOCK_STREAM;
|
||||
int family = (i % 3 == 0) ? PF_INET6 : PF_INET;
|
||||
sockets[i] = socket(family, type, IPPROTO_IP);
|
||||
}
|
||||
for (int i = 0; i < CONFIG_LWIP_MAX_SOCKETS; i++) {
|
||||
close(sockets[i]);
|
||||
}
|
||||
|
||||
// Allow LWIP tasks to finish initialising themselves
|
||||
vTaskDelay(25 / portTICK_RATE_MS);
|
||||
|
||||
printf("Note: tcpip_adapter_init() has been called. Until next reset, TCP/IP task will periodicially allocate memory and consume CPU time.\n");
|
||||
|
||||
// Reset the leak checker as LWIP allocates a lot of memory on first run
|
||||
unity_reset_leak_checks();
|
||||
}
|
||||
|
||||
// signal functions, used for sync between unity DUTs for multiple devices cases
|
||||
void unity_wait_for_signal(const char* signal_name)
|
||||
{
|
||||
printf("Waiting for signal: [%s]!\n"
|
||||
"Please press \"Enter\" key to once any board send this signal.\n", signal_name);
|
||||
wait_user_control();
|
||||
}
|
||||
|
||||
void unity_send_signal(const char* signal_name)
|
||||
{
|
||||
printf("Send signal: [%s]!\n", signal_name);
|
||||
}
|
||||
|
@ -2,16 +2,20 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "unity.h"
|
||||
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/uart.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "driver/uart.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_clk.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
#include "esp_heap_trace.h"
|
||||
#endif
|
||||
|
||||
// Pointers to the head and tail of linked list of test description structs:
|
||||
static struct test_desc_t* s_unity_tests_first = NULL;
|
||||
@ -20,6 +24,10 @@ static struct test_desc_t* s_unity_tests_last = NULL;
|
||||
// Inverse of the filter
|
||||
static bool s_invert = false;
|
||||
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
/* Each unit test is allowed to "leak" this many bytes.
|
||||
|
||||
TODO: Make this value editable by the test.
|
||||
@ -29,12 +37,52 @@ static bool s_invert = false;
|
||||
const size_t WARN_LEAK_THRESHOLD = 256;
|
||||
const size_t CRITICAL_LEAK_THRESHOLD = 4096;
|
||||
|
||||
extern int uart_rx_one_char(char *c);
|
||||
void unity_reset_leak_checks(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
heap_trace_start(HEAP_TRACE_LEAKS);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
// If heap tracing is enabled in kconfig, leak trace the test
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
const size_t num_heap_records = 80;
|
||||
static heap_trace_record_t *record_buffer;
|
||||
if (!record_buffer) {
|
||||
record_buffer = malloc(sizeof(heap_trace_record_t) * num_heap_records);
|
||||
assert(record_buffer);
|
||||
heap_trace_init_standalone(record_buffer, num_heap_records);
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("%s", ""); /* sneakily lazy-allocate the reent structure for this test task */
|
||||
get_test_data_partition(); /* allocate persistent partition table structures */
|
||||
|
||||
unity_reset_leak_checks();
|
||||
}
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
if (before_free <= after_free) {
|
||||
return;
|
||||
}
|
||||
size_t leaked = before_free - after_free;
|
||||
if (leaked < WARN_LEAK_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf("MALLOC_CAP_%s %s leak: Before %u bytes free, After %u bytes free (delta %u)\n",
|
||||
type,
|
||||
leaked < CRITICAL_LEAK_THRESHOLD ? "potential" : "critical",
|
||||
before_free, after_free, leaked);
|
||||
fflush(stdout);
|
||||
TEST_ASSERT_MESSAGE(leaked < CRITICAL_LEAK_THRESHOLD, "The test leaked too much memory");
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
@ -47,6 +95,20 @@ void tearDown(void)
|
||||
const char *real_testfile = Unity.TestFile;
|
||||
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");
|
||||
|
||||
/* check for leaks */
|
||||
#ifdef CONFIG_HEAP_TRACING
|
||||
heap_trace_stop();
|
||||
heap_trace_dump();
|
||||
#endif
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
|
||||
Unity.TestFile = real_testfile; // go back to the real filename
|
||||
}
|
||||
|
||||
@ -54,43 +116,21 @@ void unity_putc(int c)
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
putchar('\r');
|
||||
putchar('\n');
|
||||
uart_tx_one_char('\r');
|
||||
uart_tx_one_char('\n');
|
||||
}
|
||||
else if (c == '\r')
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
putchar(c);
|
||||
uart_tx_one_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
void unity_flush()
|
||||
{
|
||||
// uart_tx_wait_idle(0); // assume that output goes to UART0
|
||||
}
|
||||
|
||||
static int UART_RxString(char *s, size_t len)
|
||||
{
|
||||
size_t i = 1;
|
||||
char *s_local = s;
|
||||
|
||||
while (i < len) {
|
||||
while (uart_rx_one_char(s_local) != 0);
|
||||
|
||||
if ((*s_local == '\n') || (*s_local == '\r')) {
|
||||
break;
|
||||
}
|
||||
|
||||
s_local++;
|
||||
i++;
|
||||
}
|
||||
|
||||
s_local++;
|
||||
*s_local = '\0';
|
||||
|
||||
return 0;
|
||||
uart_tx_wait_idle(0); // assume that output goes to UART0
|
||||
}
|
||||
|
||||
void unity_testcase_register(struct test_desc_t* desc)
|
||||
@ -132,8 +172,10 @@ void multiple_function_option(const struct test_desc_t* test_ms)
|
||||
while(strlen(cmdline) == 0)
|
||||
{
|
||||
/* Flush anything already in the RX buffer */
|
||||
while(uart_rx_one_char(cmdline) == 0);
|
||||
UART_RxString(cmdline, sizeof(cmdline) - 1);
|
||||
while(uart_rx_one_char((uint8_t *) cmdline) == OK) {
|
||||
|
||||
}
|
||||
UartRxString((uint8_t*) cmdline, sizeof(cmdline) - 1);
|
||||
if(strlen(cmdline) == 0) {
|
||||
/* if input was newline, print a new menu */
|
||||
print_multiple_function_test_menu(test_ms);
|
||||
@ -152,6 +194,7 @@ static void unity_run_single_test(const struct test_desc_t* test)
|
||||
printf("Running %s...\n", test->name);
|
||||
// Unit test runner expects to see test name before the test starts
|
||||
fflush(stdout);
|
||||
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
|
||||
|
||||
Unity.TestFile = test->file;
|
||||
Unity.CurrentDetail1 = test->desc;
|
||||
@ -185,14 +228,12 @@ static void unity_run_single_test_by_index_parse(const char* filter, int index_m
|
||||
int test_index = strtol(filter, NULL, 10);
|
||||
if (test_index >= 1 && test_index <= index_max)
|
||||
{
|
||||
extern uint32_t system_get_cpu_freq(void);
|
||||
|
||||
uint32_t start;
|
||||
asm volatile ("rsr %0, CCOUNT" : "=r" (start));
|
||||
RSR(CCOUNT, start);
|
||||
unity_run_single_test_by_index(test_index - 1);
|
||||
uint32_t end;
|
||||
asm volatile ("rsr %0, CCOUNT" : "=r" (end));
|
||||
uint32_t ms = (end - start) / (system_get_cpu_freq() * 1000000 / 1000);
|
||||
RSR(CCOUNT, end);
|
||||
uint32_t ms = (end - start) / (esp_clk_cpu_freq() / 1000);
|
||||
printf("Test ran in %dms\n", ms);
|
||||
}
|
||||
}
|
||||
@ -273,6 +314,7 @@ static int print_test_menu(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\nEnter test for running.\n"); /* unit_test.py needs it for finding the end of test menu */
|
||||
return test_counter;
|
||||
}
|
||||
|
||||
@ -298,9 +340,10 @@ void unity_run_menu()
|
||||
while(strlen(cmdline) == 0)
|
||||
{
|
||||
/* Flush anything already in the RX buffer */
|
||||
while(uart_rx_one_char(cmdline) == 0);
|
||||
while(uart_rx_one_char((uint8_t *) cmdline) == OK) {
|
||||
}
|
||||
/* Read input */
|
||||
UART_RxString(cmdline, sizeof(cmdline) - 1);
|
||||
UartRxString((uint8_t*) cmdline, sizeof(cmdline) - 1);
|
||||
trim_trailing_space(cmdline);
|
||||
if(strlen(cmdline) == 0) {
|
||||
/* if input was newline, print a new menu */
|
||||
|
4
tools/unit-test-app/main/CMakeLists.txt
Normal file
4
tools/unit-test-app/main/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "app_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "")
|
||||
|
||||
register_component()
|
@ -1,11 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_system.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_config.h"
|
||||
|
||||
@ -15,7 +10,10 @@ void unityTask(void *pvParameters)
|
||||
unity_run_menu(); /* Doesn't return */
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreate(unityTask, "unityTask", 8192, NULL, UNITY_FREERTOS_PRIORITY, NULL);
|
||||
// Note: if unpinning this task, change the way run times are calculated in
|
||||
// unity_platform
|
||||
xTaskCreatePinnedToCore(unityTask, "unityTask", UNITY_FREERTOS_STACK_SIZE, NULL,
|
||||
UNITY_FREERTOS_PRIORITY, NULL, UNITY_FREERTOS_CPU);
|
||||
}
|
||||
|
14
tools/unit-test-app/partition_table_unit_test_app.csv
Normal file
14
tools/unit-test-app/partition_table_unit_test_app.csv
Normal file
@ -0,0 +1,14 @@
|
||||
# Special partition table for unit test app
|
||||
#
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||
nvs, data, nvs, 0x9000, 0x4000
|
||||
otadata, data, ota, 0xd000, 0x2000
|
||||
phy_init, data, phy, 0xf000, 0x1000
|
||||
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
|
||||
|
||||
# Note: still 1MB of a 4MB flash left free for some other purpose
|
|
7
tools/unit-test-app/sdkconfig.defaults
Normal file
7
tools/unit-test-app/sdkconfig.defaults
Normal file
@ -0,0 +1,7 @@
|
||||
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_TASK_WDT=
|
||||
CONFIG_ENABLE_PTHREAD=y
|
||||
|
Reference in New Issue
Block a user