mirror of https://github.com/ARMmbed/mbed-os.git
Updates to make utest thread safe and thus run when compiled with both gcc
and armcc. 1. Remove use of printf from all code that may be directly or indirectly invoked from an interrupt context, 2. For occasions where a printf is required and the code in question may run both inside and outside an interrupt context, add a new interrupt safe version of printf, utest_printf(). This function will only pass its arguments down to printf if interrupts are not disabled. 3. In harness.cpp, is_busy() , fix a bug where the function can return leaving interrupts disabled. 4. In unity_handler.cpp add a new function, utest_safe_putc(), This is used to override the default putc() function used by Unity. This version checks that the current code does not have interrupts disabled prior to outputting the character to the serial port. This overriding is enabled by adding unity_config.h to the unity code and redefining UNITY_OUTPUT_CHAR to utest_safe_putc(). The new config file is included in the build by adding the define UNITY_INCLUDE_CONFIG_H. The Unity changes are submitted under a separate PR. This change ensures that any Unity ASSERTS executed from within interrupt context are safe.
parent
d4415eeeac
commit
0ccdfe3ca2
|
@ -40,7 +40,7 @@ static void test_failure_handler(const failure_t failure) {
|
|||
UTEST_LOG_FUNCTION();
|
||||
if (failure.location == LOCATION_TEST_SETUP || failure.location == LOCATION_TEST_TEARDOWN) {
|
||||
verbose_test_failure_handler(failure);
|
||||
printf("{{failure}}\n{{end}}\n");
|
||||
utest_printf("{{failure}}\n{{end}}\n");
|
||||
while(1) ;
|
||||
}
|
||||
}
|
||||
|
@ -49,25 +49,25 @@ static void test_failure_handler(const failure_t failure) {
|
|||
utest::v1::status_t utest::v1::verbose_test_setup_handler(const size_t number_of_cases)
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
printf(">>> Running %u test cases...\n", number_of_cases);
|
||||
utest_printf(">>> Running %u test cases...\n", number_of_cases);
|
||||
return STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
void utest::v1::verbose_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure)
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
printf("\n>>> Test cases: %u passed, %u failed", passed, failed);
|
||||
utest_printf("\n>>> Test cases: %u passed, %u failed", passed, failed);
|
||||
if (failure.reason == REASON_NONE) {
|
||||
printf("\n");
|
||||
utest_printf("\n");
|
||||
} else {
|
||||
printf(" with reason '%s'\n", stringify(failure.reason));
|
||||
utest_printf(" with reason '%s'\n", stringify(failure.reason));
|
||||
}
|
||||
if (failed) printf(">>> TESTS FAILED!\n");
|
||||
if (failed) utest_printf(">>> TESTS FAILED!\n");
|
||||
}
|
||||
|
||||
void utest::v1::verbose_test_failure_handler(const failure_t failure)
|
||||
{
|
||||
printf(">>> failure with reason '%s' during '%s'\n", stringify(failure.reason), stringify(failure.location));
|
||||
utest_printf(">>> failure with reason '%s' during '%s'\n", stringify(failure.reason), stringify(failure.location));
|
||||
|
||||
}
|
||||
|
||||
|
@ -75,18 +75,18 @@ void utest::v1::verbose_test_failure_handler(const failure_t failure)
|
|||
utest::v1::status_t utest::v1::verbose_case_setup_handler(const Case *const source, const size_t index_of_case)
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
printf("\n>>> Running case #%u: '%s'...\n", index_of_case + 1, source->get_description());
|
||||
utest_printf("\n>>> Running case #%u: '%s'...\n", index_of_case + 1, source->get_description());
|
||||
return STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
utest::v1::status_t utest::v1::verbose_case_teardown_handler(const Case *const source, const size_t passed, const size_t failed, const failure_t failure)
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
printf(">>> '%s': %u passed, %u failed", source->get_description(), passed, failed);
|
||||
utest_printf(">>> '%s': %u passed, %u failed", source->get_description(), passed, failed);
|
||||
if (failure.reason == REASON_NONE) {
|
||||
printf("\n");
|
||||
utest_printf("\n");
|
||||
} else {
|
||||
printf(" with reason '%s'\n", stringify(failure.reason));
|
||||
utest_printf(" with reason '%s'\n", stringify(failure.reason));
|
||||
}
|
||||
return STATUS_CONTINUE;
|
||||
}
|
||||
|
|
|
@ -56,11 +56,11 @@ const handlers_t utest::v1::selftest_handlers = {
|
|||
};
|
||||
|
||||
|
||||
// --- SPECIAL HANDLERS ---
|
||||
// --- SPECIAL HANDLERS ---
|
||||
static utest::v1::status_t unknown_test_setup_handler(const size_t) {
|
||||
UTEST_LOG_FUNCTION();
|
||||
printf(">>> I do not know how to tell greentea that the test started, since\n");
|
||||
printf(">>> you forgot to override the `test_setup_handler` in your specification.\n");
|
||||
utest_printf(">>> I do not know how to tell greentea that the test started, since\n");
|
||||
utest_printf(">>> you forgot to override the `test_setup_handler` in your specification.\n");
|
||||
|
||||
return STATUS_ABORT;
|
||||
}
|
||||
|
|
|
@ -205,7 +205,6 @@ void Harness::schedule_next_case()
|
|||
location = LOCATION_CASE_TEARDOWN;
|
||||
|
||||
if (handlers.case_teardown) {
|
||||
// printf("Schedule next case: case_passed = %d, case_failed = %d\n", case_passed, case_failed);
|
||||
utest::v1::status_t status = handlers.case_teardown(case_current, case_passed, case_failed,
|
||||
case_failed ? failure_t(REASON_CASES, LOCATION_UNKNOWN) : failure_t(REASON_NONE));
|
||||
if (status < STATUS_CONTINUE) raise_failure(REASON_CASE_TEARDOWN);
|
||||
|
@ -254,10 +253,8 @@ void Harness::validate_callback(const control_t control)
|
|||
UTEST_ENTER_CRITICAL_SECTION;
|
||||
case_validation_count++;
|
||||
|
||||
// printf("validate_callback: case_validation_count = %d\n", case_validation_count);
|
||||
if (case_timeout_handle != NULL || case_control.timeout == TIMEOUT_FOREVER)
|
||||
{
|
||||
// printf("Cancelling scheduled callback\n");
|
||||
scheduler.cancel(case_timeout_handle);
|
||||
case_timeout_handle = NULL;
|
||||
control_t merged_control = case_control + control;
|
||||
|
@ -272,10 +269,12 @@ bool Harness::is_busy()
|
|||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
UTEST_ENTER_CRITICAL_SECTION;
|
||||
if (!test_cases) return false;
|
||||
if (!case_current) return false;
|
||||
|
||||
bool res = (case_current < (test_cases + test_length));
|
||||
bool res = false;
|
||||
|
||||
if (test_cases && case_current) {
|
||||
res = (case_current < (test_cases + test_length));
|
||||
}
|
||||
|
||||
UTEST_LEAVE_CRITICAL_SECTION;
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ utest_v1_scheduler_t utest_v1_get_scheduler()
|
|||
#else
|
||||
# include "mbed.h"
|
||||
#endif
|
||||
|
||||
// only one callback is active at any given time
|
||||
static volatile utest_v1_harness_callback_t minimal_callback;
|
||||
static volatile utest_v1_harness_callback_t ticker_callback;
|
||||
|
@ -70,7 +71,6 @@ Timeout utest_timeout_object;
|
|||
static void ticker_handler()
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
//printf("\t\t>>> Ticker callback fired for %p.\n", ticker_callback);
|
||||
minimal_callback = ticker_callback;
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,6 @@ static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, ti
|
|||
UTEST_LOG_FUNCTION();
|
||||
timestamp_t delay_us = delay_ms *1000;
|
||||
|
||||
//printf("\t\t>>> Schedule %p with %ums delay => %p.\n", callback, (unsigned int)delay_ms, (void*)1);
|
||||
if (delay_ms) {
|
||||
ticker_callback = callback;
|
||||
// fire the interrupt in 1000us * delay_ms
|
||||
|
@ -96,15 +95,12 @@ static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, ti
|
|||
minimal_callback = callback;
|
||||
}
|
||||
|
||||
//printf("Minimal callback = %p, ticker_callback = %p\n", minimal_callback, ticker_callback);
|
||||
|
||||
// return a bogus handle
|
||||
return (void*)1;
|
||||
}
|
||||
static int32_t utest_us_ticker_cancel(void *handle)
|
||||
{
|
||||
UTEST_LOG_FUNCTION();
|
||||
//printf("\t\t>>> Cancel %p => %u\n", handle, (unsigned int)0);
|
||||
(void) handle;
|
||||
utest_timeout_object.detach();
|
||||
return 0;
|
||||
|
@ -117,7 +113,6 @@ static int32_t utest_us_ticker_run()
|
|||
// check if a new callback has been set
|
||||
if (minimal_callback)
|
||||
{
|
||||
//printf("\t\t>>> Firing callback %p\n", minimal_callback);
|
||||
// copy the callback
|
||||
utest_v1_harness_callback_t callback = minimal_callback;
|
||||
// reset the shared callback
|
||||
|
@ -128,6 +123,22 @@ static int32_t utest_us_ticker_run()
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int utest_printf(char *str, ...)
|
||||
{
|
||||
volatile uint32_t primask = __get_PRIMASK();\
|
||||
if ( (primask & 0x1) == 0){ \
|
||||
va_list vargs;
|
||||
|
||||
va_start(vargs, str);
|
||||
vprintf(str, vargs);
|
||||
va_end(vargs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
static const utest_v1_scheduler_t utest_v1_scheduler =
|
||||
{
|
||||
|
|
|
@ -51,15 +51,15 @@ void utest_dump_trace()
|
|||
{
|
||||
unsigned current = (trace_index == 0) ? UTEST_MAX_BACKTRACE - 1 : trace_index - 1;
|
||||
|
||||
printf("==================================================================\n");
|
||||
printf("Utest back trace: Total calls logged = %u.\n", total_calls);
|
||||
printf("==================================================================\n");
|
||||
utest_printf("==================================================================\n");
|
||||
utest_printf("Utest back trace: Total calls logged = %u.\n", total_calls);
|
||||
utest_printf("==================================================================\n");
|
||||
while (current != trace_index) {
|
||||
|
||||
printf("%u > %s\n", current, utest_trace[current].c_str());
|
||||
utest_printf("%u > %s\n", current, utest_trace[current].c_str());
|
||||
current = (current == 0) ? UTEST_MAX_BACKTRACE - 1 : current - 1;
|
||||
}
|
||||
printf("==================================================================\n");
|
||||
utest_printf("==================================================================\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,3 +32,14 @@ void utest_unity_ignore_failure(void)
|
|||
UTEST_LOG_FUNCTION();
|
||||
utest::v1::Harness::raise_failure(utest::v1::failure_reason_t(utest::v1::REASON_ASSERTION | utest::v1::REASON_IGNORE));
|
||||
}
|
||||
|
||||
void utest_safe_putc(int chr)
|
||||
{
|
||||
volatile uint32_t primask = __get_PRIMASK();
|
||||
if ( (primask & 0x1) == 0){
|
||||
(void)putchar(chr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ extern "C" {
|
|||
/// must be implemented by the port
|
||||
void utest_v1_enter_critical_section(void);
|
||||
void utest_v1_leave_critical_section(void);
|
||||
int utest_printf(char *str, ...);
|
||||
|
||||
/// This is the default scheduler implementation used by the harness.
|
||||
utest_v1_scheduler_t utest_v1_get_scheduler(void);
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#define UTEST_UNITY_ASSERT_FAILURE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "cmsis.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -31,6 +33,8 @@ void utest_unity_assert_failure(void);
|
|||
/// this function is called from the unity module when an assertion failed, but is ignored.
|
||||
void utest_unity_ignore_failure(void);
|
||||
|
||||
void utest_safe_putc(int chr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue