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.
Anna Bridge 2016-06-07 15:56:26 +01:00
parent d4415eeeac
commit 0ccdfe3ca2
8 changed files with 58 additions and 32 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 =
{

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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