mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
unit-test-app: support multiple devices test cases:
current unit-test-app don't support test components need to communicate with each other (like GPIO, SPI ...). Now we add multiple devices mode to unit test app, support writing and running test with multiple DUTs. please refer to `docs/api-guides/unit-tests.rst` for detail.
This commit is contained in:
@@ -36,8 +36,48 @@ void ref_clock_init();
|
||||
*/
|
||||
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 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);
|
||||
|
@@ -20,21 +20,50 @@
|
||||
#define UNITY_OUTPUT_FLUSH unity_flush
|
||||
|
||||
// Define helpers to register test cases from multiple files
|
||||
|
||||
#define UNITY_EXPAND2(a, b) a ## b
|
||||
#define UNITY_EXPAND(a, b) UNITY_EXPAND2(a, b)
|
||||
#define UNITY_TEST_UID(what) UNITY_EXPAND(what, __LINE__)
|
||||
|
||||
#define UNITY_TEST_REG_HELPER reg_helper ## UNITY_TEST_UID
|
||||
#define UNITY_TEST_DESC_UID desc ## UNITY_TEST_UID
|
||||
|
||||
|
||||
// get count of __VA_ARGS__
|
||||
#define PP_NARG(...) \
|
||||
PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
|
||||
#define PP_NARG_(...) \
|
||||
PP_ARG_N(__VA_ARGS__)
|
||||
#define PP_ARG_N( \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
|
||||
#define PP_RSEQ_N() 9,8,7,6,5,4,3,2,1,0
|
||||
|
||||
// support max 5 test func now
|
||||
#define FN_NAME_SET_1(a) {#a}
|
||||
#define FN_NAME_SET_2(a, b) {#a, #b}
|
||||
#define FN_NAME_SET_3(a, b, c) {#a, #b, #c}
|
||||
#define FN_NAME_SET_4(a, b, c, d) {#a, #b, #c, #d}
|
||||
#define FN_NAME_SET_5(a, b, c, d, e) {#a, #b, #c, #d, #e}
|
||||
|
||||
#define FN_NAME_SET2(n) FN_NAME_SET_##n
|
||||
#define FN_NAME_SET(n, ...) FN_NAME_SET2(n)(__VA_ARGS__)
|
||||
|
||||
#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__)
|
||||
|
||||
|
||||
typedef void (* test_func)(void);
|
||||
|
||||
struct test_desc_t
|
||||
{
|
||||
const char* name;
|
||||
const char* desc;
|
||||
void (*fn)(void);
|
||||
const char* file;
|
||||
int line;
|
||||
struct test_desc_t* next;
|
||||
const char* name;
|
||||
const char* desc;
|
||||
test_func* fn;
|
||||
const char* file;
|
||||
int line;
|
||||
uint8_t test_fn_count;
|
||||
char ** test_fn_name;
|
||||
struct test_desc_t* next;
|
||||
};
|
||||
|
||||
void unity_testcase_register(struct test_desc_t* desc);
|
||||
@@ -46,7 +75,7 @@ void unity_run_tests_with_filter(const char* filter);
|
||||
void unity_run_all_tests();
|
||||
|
||||
/* Test case macro, a-la CATCH framework.
|
||||
First argument is a free-form description,
|
||||
First argument is a free-form description,
|
||||
second argument is (by convention) a list of identifiers, each one in square brackets.
|
||||
Identifiers are used to group related tests, or tests with specific properties.
|
||||
Use like:
|
||||
@@ -56,21 +85,51 @@ void unity_run_all_tests();
|
||||
// test goes here
|
||||
}
|
||||
*/
|
||||
|
||||
#define TEST_CASE(name_, desc_) \
|
||||
static void UNITY_TEST_UID(test_func_) (void); \
|
||||
static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
|
||||
{ \
|
||||
static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
|
||||
.name = name_, \
|
||||
.desc = desc_, \
|
||||
.fn = &UNITY_TEST_UID(test_func_), \
|
||||
.file = __FILE__, \
|
||||
.line = __LINE__, \
|
||||
.next = NULL \
|
||||
}; \
|
||||
unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
|
||||
}\
|
||||
static void UNITY_TEST_UID(test_func_) (void)
|
||||
static void UNITY_TEST_UID(test_func_) (void); \
|
||||
static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
|
||||
{ \
|
||||
static test_func test_fn_[] = {&UNITY_TEST_UID(test_func_)}; \
|
||||
static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
|
||||
.name = name_, \
|
||||
.desc = desc_, \
|
||||
.fn = test_fn_, \
|
||||
.file = __FILE__, \
|
||||
.line = __LINE__, \
|
||||
.test_fn_count = 1, \
|
||||
.test_fn_name = NULL, \
|
||||
.next = NULL \
|
||||
}; \
|
||||
unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
|
||||
}\
|
||||
static void UNITY_TEST_UID(test_func_) (void)
|
||||
|
||||
|
||||
/*
|
||||
* First argument is a free-form description,
|
||||
* second argument is (by convention) a list of identifiers, each one in square brackets.
|
||||
* subsequent arguments are names of test functions for different DUTs
|
||||
* e.g:
|
||||
* TEST_CASE_MULTIPLE_DEVICES("master and slave spi","[spi][test_env=UT_T2_1]", master_test, slave_test);
|
||||
* */
|
||||
|
||||
#define TEST_CASE_MULTIPLE_DEVICES(name_, desc_, ...) \
|
||||
UNITY_TEST_FN_SET(__VA_ARGS__); \
|
||||
static void __attribute__((constructor)) UNITY_TEST_UID(test_reg_helper_) () \
|
||||
{ \
|
||||
static struct test_desc_t UNITY_TEST_UID(test_desc_) = { \
|
||||
.name = name_, \
|
||||
.desc = desc_, \
|
||||
.fn = UNITY_TEST_UID(test_functions), \
|
||||
.file = __FILE__, \
|
||||
.line = __LINE__, \
|
||||
.test_fn_count = PP_NARG(__VA_ARGS__), \
|
||||
.test_fn_name = UNITY_TEST_UID(test_fn_name), \
|
||||
.next = NULL \
|
||||
}; \
|
||||
unity_testcase_register( & UNITY_TEST_UID(test_desc_) ); \
|
||||
}
|
||||
/**
|
||||
* Note: initialization of test_desc_t fields above has to be done exactly
|
||||
* in the same order as the fields are declared in the structure.
|
||||
|
@@ -12,8 +12,11 @@
|
||||
// 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"
|
||||
|
||||
const esp_partition_t *get_test_data_partition()
|
||||
{
|
||||
@@ -23,3 +26,31 @@ const esp_partition_t *get_test_data_partition()
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@@ -145,12 +145,57 @@ void unity_testcase_register(struct test_desc_t* desc)
|
||||
}
|
||||
}
|
||||
|
||||
/* print the multiple devices case name and its sub-menu
|
||||
* e.g:
|
||||
* (1) spi master/slave case
|
||||
* (1)master case
|
||||
* (2)slave case
|
||||
* */
|
||||
static void print_multiple_devices_test_menu(const struct test_desc_t* test_ms)
|
||||
{
|
||||
unity_printf("%s\n", test_ms->name);
|
||||
for (int i = 0; i < test_ms->test_fn_count; i++)
|
||||
{
|
||||
unity_printf("\t(%d)\t\"%s\"\n", i+1, test_ms->test_fn_name[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void multiple_devices_option(const struct test_desc_t* test_ms)
|
||||
{
|
||||
int selection;
|
||||
char cmdline[256] = {0};
|
||||
|
||||
print_multiple_devices_test_menu(test_ms);
|
||||
while(strlen(cmdline) == 0)
|
||||
{
|
||||
/* Flush anything already in the RX buffer */
|
||||
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_devices_test_menu(test_ms);
|
||||
}
|
||||
}
|
||||
selection = atoi((const char *) cmdline) - 1;
|
||||
if(selection >= 0 && selection < test_ms->test_fn_count) {
|
||||
UnityDefaultTestRun(test_ms->fn[selection], test_ms->name, test_ms->line);
|
||||
} else {
|
||||
printf("Invalid selection, your should input number 1-%d!", test_ms->test_fn_count);
|
||||
}
|
||||
}
|
||||
|
||||
static void unity_run_single_test(const struct test_desc_t* test)
|
||||
{
|
||||
printf("Running %s...\n", test->name);
|
||||
Unity.TestFile = test->file;
|
||||
Unity.CurrentDetail1 = test->desc;
|
||||
UnityDefaultTestRun(test->fn, test->name, test->line);
|
||||
if(test->test_fn_count == 1) {
|
||||
UnityDefaultTestRun(test->fn[0], test->name, test->line);
|
||||
} else {
|
||||
multiple_devices_option(test);
|
||||
}
|
||||
}
|
||||
|
||||
static void unity_run_single_test_by_index(int index)
|
||||
@@ -158,6 +203,7 @@ static void unity_run_single_test_by_index(int index)
|
||||
const struct test_desc_t* test;
|
||||
for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index)
|
||||
{
|
||||
|
||||
}
|
||||
if (test != NULL)
|
||||
{
|
||||
@@ -201,7 +247,7 @@ static void unity_run_single_test_by_name(const char* filter)
|
||||
{
|
||||
unity_run_single_test(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unity_run_all_tests()
|
||||
@@ -253,8 +299,15 @@ static int print_test_menu(void)
|
||||
test = test->next, ++test_counter)
|
||||
{
|
||||
unity_printf("(%d)\t\"%s\" %s\n", test_counter + 1, test->name, test->desc);
|
||||
}
|
||||
return test_counter;
|
||||
if(test->test_fn_count > 1)
|
||||
{
|
||||
for (int i = 0; i < test->test_fn_count; i++)
|
||||
{
|
||||
unity_printf("\t(%d)\t\"%s\"\n", i+1, test->test_fn_name[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return test_counter;
|
||||
}
|
||||
|
||||
static int get_test_count(void)
|
||||
|
Reference in New Issue
Block a user