/* * SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include "unity.h" #include "esp_system.h" /* similar to UNITY_PRINT_EOL */ #define UNITY_PRINT_TAB() UNITY_OUTPUT_CHAR('\t') // Pointers to the head and tail of linked list of test description structs: static test_desc_t *s_unity_tests_first = NULL; static test_desc_t *s_unity_tests_last = NULL; void unity_testcase_register(test_desc_t *desc) { if (!s_unity_tests_first) { s_unity_tests_first = desc; s_unity_tests_last = desc; } else { test_desc_t *temp = s_unity_tests_first; s_unity_tests_first = desc; s_unity_tests_first->next = temp; } } /* print the multiple function case name and its sub-menu * e.g: * (1) spi master/slave case * (1)master case * (2)slave case * */ static void print_multiple_function_test_menu(const test_desc_t *test_ms) { UnityPrint(test_ms->name); UNITY_PRINT_EOL(); for (int i = 0; i < test_ms->test_fn_count; i++) { UNITY_PRINT_TAB(); UnityPrint("("); UnityPrintNumberUnsigned(i + 1); UnityPrint(")"); UNITY_PRINT_TAB(); UnityPrint("\""); UnityPrint(test_ms->test_fn_name[i]); UnityPrint("\""); UNITY_PRINT_EOL(); } } /* * This function looks like UnityDefaultTestRun function only without UNITY_CLR_DETAILS. * void UnityDefaultTestRun(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) * was moved from `components/unity/unity/src/unity.c` to here. */ static void unity_default_test_run(UnityTestFunction Func, const char* FuncName, const int FuncLineNum) { Unity.CurrentTestName = FuncName; Unity.CurrentTestLineNumber = (UNITY_LINE_TYPE)FuncLineNum; Unity.NumberOfTests++; if (TEST_PROTECT()) { setUp(); Func(); } if (TEST_PROTECT()) { tearDown(); } UnityConcludeTest(); } static int multiple_function_option(const test_desc_t *test_ms) { int selection; char cmdline[256] = {0}; print_multiple_function_test_menu(test_ms); while (strlen(cmdline) == 0) { unity_gets(cmdline, sizeof(cmdline)); if (strlen(cmdline) == 0) { /* if input was newline, print a new menu */ print_multiple_function_test_menu(test_ms); } } selection = atoi((const char *) cmdline) - 1; if (selection >= 0 && selection < test_ms->test_fn_count) { UnityPrint("Running stage "); UnityPrintNumber(selection + 1); UnityPrint("..."); UNITY_PRINT_EOL(); // Unit test runner expects to see test name before the test starts UNITY_OUTPUT_FLUSH(); unity_default_test_run(test_ms->fn[selection], test_ms->name, test_ms->line); } else { UnityPrint("Invalid selection, your should input number 1-"); UnityPrintNumber(test_ms->test_fn_count); UNITY_PRINT_EOL(); } return selection; } static void unity_run_single_test(const test_desc_t *test) { UnityPrint("Running "); UnityPrint(test->name); UnityPrint("..."); UNITY_PRINT_EOL(); // Unit test runner expects to see test name before the test starts UNITY_OUTPUT_FLUSH(); Unity.TestFile = test->file; Unity.CurrentDetail1 = test->desc; bool reset_after_test = strstr(Unity.CurrentDetail1, "[leaks") != NULL; bool multi_device = strstr(Unity.CurrentDetail1, "[multi_device]") != NULL; if (test->test_fn_count == 1) { unity_default_test_run(test->fn[0], test->name, test->line); } else { int selection = multiple_function_option(test); if (reset_after_test && multi_device == false) { if (selection != (test->test_fn_count - 1)) { // to do a reset for all stages except the last stage. esp_restart(); } } } if (reset_after_test) { // print a result of test before to do reset for the last stage. UNITY_END(); UnityPrint("Enter next test, or 'enter' to see menu"); UNITY_PRINT_EOL(); UNITY_OUTPUT_FLUSH(); esp_restart(); } } void unity_run_test_by_index(int index) { const test_desc_t *test; for (test = s_unity_tests_first; test != NULL && index != 0; test = test->next, --index) { ; } if (test != NULL) { unity_run_single_test(test); } } static void unity_run_single_test_by_index_parse(const char *filter, int index_max) { int test_index = strtol(filter, NULL, 10); if (test_index >= 1 && test_index <= index_max) { UNITY_EXEC_TIME_START(); unity_run_test_by_index(test_index - 1); UNITY_EXEC_TIME_STOP(); UnityPrint("Test ran in "); UnityPrintNumberUnsigned(UNITY_EXEC_TIME_MS()); UnityPrint("ms"); UNITY_PRINT_EOL(); UNITY_OUTPUT_FLUSH(); } } void unity_run_test_by_name(const char *name) { for (const test_desc_t *test = s_unity_tests_first; test != NULL; test = test->next) { if (strcmp(test->name, name) == 0) { unity_run_single_test(test); } } } void unity_run_all_tests(void) { for (const test_desc_t *test = s_unity_tests_first; test != NULL; test = test->next) { unity_run_single_test(test); } } void unity_run_tests_by_tag(const char *tag, bool invert) { UnityPrint("Running tests "); if (invert) { UnityPrint("NOT "); } UnityPrint("matching '"); UnityPrint(tag); UnityPrint("'..."); UNITY_PRINT_EOL(); for (const test_desc_t *test = s_unity_tests_first; test != NULL; test = test->next) { if ((strstr(test->desc, tag) != NULL) == !invert) { unity_run_single_test(test); } } } static void trim_trailing_space(char *str) { char *end = str + strlen(str) - 1; while (end >= str && isspace((int) *end)) { *end = 0; --end; } } static int print_test_menu(void) { int test_counter = 0; UNITY_PRINT_EOL(); UNITY_PRINT_EOL(); UnityPrint("Here's the test menu, pick your combo:"); UNITY_PRINT_EOL(); for (const test_desc_t *test = s_unity_tests_first; test != NULL; test = test->next, ++test_counter) { UnityPrint("("); UnityPrintNumber(test_counter + 1); UnityPrint(")"); UNITY_PRINT_TAB(); UnityPrint("\""); UnityPrint(test->name); UnityPrint("\" "); UnityPrint(test->desc); UNITY_PRINT_EOL(); if (test->test_fn_count > 1) { for (int i = 0; i < test->test_fn_count; i++) { UNITY_PRINT_TAB(); UnityPrint("("); UnityPrintNumber(i + 1); UnityPrint(")"); UNITY_PRINT_TAB(); UnityPrint("\""); UnityPrint(test->test_fn_name[i]); UnityPrint("\""); UNITY_PRINT_EOL(); } } } UNITY_PRINT_EOL(); UnityPrint("Enter test for running."); /* unit_test.py needs it for finding the end of test menu */ UNITY_PRINT_EOL(); UNITY_OUTPUT_FLUSH(); return test_counter; } int unity_get_test_count(void) { int test_counter = 0; for (const test_desc_t *test = s_unity_tests_first; test != NULL; test = test->next) { ++test_counter; } return test_counter; } void unity_run_menu(void) { UNITY_PRINT_EOL(); UNITY_PRINT_EOL(); UnityPrint("Press ENTER to see the list of tests."); UNITY_PRINT_EOL(); int test_count = unity_get_test_count(); while (true) { char cmdline[256] = { 0 }; while (strlen(cmdline) == 0) { unity_gets(cmdline, sizeof(cmdline)); trim_trailing_space(cmdline); if (strlen(cmdline) == 0) { /* if input was newline, print a new menu */ print_test_menu(); } } /*use '-' to show test history. Need to do it before UNITY_BEGIN cleanup history */ if (cmdline[0] == '-') { UNITY_END(); continue; } UNITY_BEGIN(); size_t idx = 0; bool invert = false; if (cmdline[idx] == '!') { invert = true; ++idx; } if (cmdline[idx] == '*') { unity_run_all_tests(); } else if (cmdline[idx] == '[') { unity_run_tests_by_tag(cmdline + idx, invert); } else if (cmdline[idx] == '"') { char* end = strrchr(cmdline, '"'); if (end > &cmdline[idx]) { *end = 0; unity_run_test_by_name(cmdline + idx + 1); } } else if (isdigit((unsigned char)cmdline[idx])) { unity_run_single_test_by_index_parse(cmdline + idx, test_count); } UNITY_END(); UnityPrint("Enter next test, or 'enter' to see menu"); UNITY_PRINT_EOL(); UNITY_OUTPUT_FLUSH(); } } bool unity_get_test_info(int test_index, test_desc_t* out_info) { if (test_index < 0) { return false; } const test_desc_t *test; for (test = s_unity_tests_first; test != NULL && test_index != 0; test = test->next, --test_index) { ; } if (test == NULL) { return false; } *out_info = *test; return true; }