Skip to content

Commit 0ccdfe3

Browse files
committed
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.
1 parent d4415ee commit 0ccdfe3

File tree

8 files changed

+58
-32
lines changed

8 files changed

+58
-32
lines changed

frameworks/utest/source/default_handlers.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ static void test_failure_handler(const failure_t failure) {
4040
UTEST_LOG_FUNCTION();
4141
if (failure.location == LOCATION_TEST_SETUP || failure.location == LOCATION_TEST_TEARDOWN) {
4242
verbose_test_failure_handler(failure);
43-
printf("{{failure}}\n{{end}}\n");
43+
utest_printf("{{failure}}\n{{end}}\n");
4444
while(1) ;
4545
}
4646
}
@@ -49,44 +49,44 @@ static void test_failure_handler(const failure_t failure) {
4949
utest::v1::status_t utest::v1::verbose_test_setup_handler(const size_t number_of_cases)
5050
{
5151
UTEST_LOG_FUNCTION();
52-
printf(">>> Running %u test cases...\n", number_of_cases);
52+
utest_printf(">>> Running %u test cases...\n", number_of_cases);
5353
return STATUS_CONTINUE;
5454
}
5555

5656
void utest::v1::verbose_test_teardown_handler(const size_t passed, const size_t failed, const failure_t failure)
5757
{
5858
UTEST_LOG_FUNCTION();
59-
printf("\n>>> Test cases: %u passed, %u failed", passed, failed);
59+
utest_printf("\n>>> Test cases: %u passed, %u failed", passed, failed);
6060
if (failure.reason == REASON_NONE) {
61-
printf("\n");
61+
utest_printf("\n");
6262
} else {
63-
printf(" with reason '%s'\n", stringify(failure.reason));
63+
utest_printf(" with reason '%s'\n", stringify(failure.reason));
6464
}
65-
if (failed) printf(">>> TESTS FAILED!\n");
65+
if (failed) utest_printf(">>> TESTS FAILED!\n");
6666
}
6767

6868
void utest::v1::verbose_test_failure_handler(const failure_t failure)
6969
{
70-
printf(">>> failure with reason '%s' during '%s'\n", stringify(failure.reason), stringify(failure.location));
70+
utest_printf(">>> failure with reason '%s' during '%s'\n", stringify(failure.reason), stringify(failure.location));
7171

7272
}
7373

7474
// --- VERBOSE CASE HANDLERS ---
7575
utest::v1::status_t utest::v1::verbose_case_setup_handler(const Case *const source, const size_t index_of_case)
7676
{
7777
UTEST_LOG_FUNCTION();
78-
printf("\n>>> Running case #%u: '%s'...\n", index_of_case + 1, source->get_description());
78+
utest_printf("\n>>> Running case #%u: '%s'...\n", index_of_case + 1, source->get_description());
7979
return STATUS_CONTINUE;
8080
}
8181

8282
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)
8383
{
8484
UTEST_LOG_FUNCTION();
85-
printf(">>> '%s': %u passed, %u failed", source->get_description(), passed, failed);
85+
utest_printf(">>> '%s': %u passed, %u failed", source->get_description(), passed, failed);
8686
if (failure.reason == REASON_NONE) {
87-
printf("\n");
87+
utest_printf("\n");
8888
} else {
89-
printf(" with reason '%s'\n", stringify(failure.reason));
89+
utest_printf(" with reason '%s'\n", stringify(failure.reason));
9090
}
9191
return STATUS_CONTINUE;
9292
}

frameworks/utest/source/greentea_handlers.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ const handlers_t utest::v1::selftest_handlers = {
5656
};
5757

5858

59-
// --- SPECIAL HANDLERS ---
59+
// --- SPECIAL HANDLERS ---
6060
static utest::v1::status_t unknown_test_setup_handler(const size_t) {
6161
UTEST_LOG_FUNCTION();
62-
printf(">>> I do not know how to tell greentea that the test started, since\n");
63-
printf(">>> you forgot to override the `test_setup_handler` in your specification.\n");
62+
utest_printf(">>> I do not know how to tell greentea that the test started, since\n");
63+
utest_printf(">>> you forgot to override the `test_setup_handler` in your specification.\n");
6464

6565
return STATUS_ABORT;
6666
}

frameworks/utest/source/harness.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ void Harness::schedule_next_case()
205205
location = LOCATION_CASE_TEARDOWN;
206206

207207
if (handlers.case_teardown) {
208-
// printf("Schedule next case: case_passed = %d, case_failed = %d\n", case_passed, case_failed);
209208
utest::v1::status_t status = handlers.case_teardown(case_current, case_passed, case_failed,
210209
case_failed ? failure_t(REASON_CASES, LOCATION_UNKNOWN) : failure_t(REASON_NONE));
211210
if (status < STATUS_CONTINUE) raise_failure(REASON_CASE_TEARDOWN);
@@ -254,10 +253,8 @@ void Harness::validate_callback(const control_t control)
254253
UTEST_ENTER_CRITICAL_SECTION;
255254
case_validation_count++;
256255

257-
// printf("validate_callback: case_validation_count = %d\n", case_validation_count);
258256
if (case_timeout_handle != NULL || case_control.timeout == TIMEOUT_FOREVER)
259257
{
260-
// printf("Cancelling scheduled callback\n");
261258
scheduler.cancel(case_timeout_handle);
262259
case_timeout_handle = NULL;
263260
control_t merged_control = case_control + control;
@@ -272,10 +269,12 @@ bool Harness::is_busy()
272269
{
273270
UTEST_LOG_FUNCTION();
274271
UTEST_ENTER_CRITICAL_SECTION;
275-
if (!test_cases) return false;
276-
if (!case_current) return false;
277-
278-
bool res = (case_current < (test_cases + test_length));
272+
bool res = false;
273+
274+
if (test_cases && case_current) {
275+
res = (case_current < (test_cases + test_length));
276+
}
277+
279278
UTEST_LEAVE_CRITICAL_SECTION;
280279
return res;
281280
}

frameworks/utest/source/shim.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ utest_v1_scheduler_t utest_v1_get_scheduler()
6060
#else
6161
# include "mbed.h"
6262
#endif
63+
6364
// only one callback is active at any given time
6465
static volatile utest_v1_harness_callback_t minimal_callback;
6566
static volatile utest_v1_harness_callback_t ticker_callback;
@@ -70,7 +71,6 @@ Timeout utest_timeout_object;
7071
static void ticker_handler()
7172
{
7273
UTEST_LOG_FUNCTION();
73-
//printf("\t\t>>> Ticker callback fired for %p.\n", ticker_callback);
7474
minimal_callback = ticker_callback;
7575
}
7676

@@ -85,7 +85,6 @@ static void *utest_us_ticker_post(const utest_v1_harness_callback_t callback, ti
8585
UTEST_LOG_FUNCTION();
8686
timestamp_t delay_us = delay_ms *1000;
8787

88-
//printf("\t\t>>> Schedule %p with %ums delay => %p.\n", callback, (unsigned int)delay_ms, (void*)1);
8988
if (delay_ms) {
9089
ticker_callback = callback;
9190
// 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
9695
minimal_callback = callback;
9796
}
9897

99-
//printf("Minimal callback = %p, ticker_callback = %p\n", minimal_callback, ticker_callback);
100-
10198
// return a bogus handle
10299
return (void*)1;
103100
}
104101
static int32_t utest_us_ticker_cancel(void *handle)
105102
{
106103
UTEST_LOG_FUNCTION();
107-
//printf("\t\t>>> Cancel %p => %u\n", handle, (unsigned int)0);
108104
(void) handle;
109105
utest_timeout_object.detach();
110106
return 0;
@@ -117,7 +113,6 @@ static int32_t utest_us_ticker_run()
117113
// check if a new callback has been set
118114
if (minimal_callback)
119115
{
120-
//printf("\t\t>>> Firing callback %p\n", minimal_callback);
121116
// copy the callback
122117
utest_v1_harness_callback_t callback = minimal_callback;
123118
// reset the shared callback
@@ -128,6 +123,22 @@ static int32_t utest_us_ticker_run()
128123
}
129124
return 0;
130125
}
126+
127+
int utest_printf(char *str, ...)
128+
{
129+
volatile uint32_t primask = __get_PRIMASK();\
130+
if ( (primask & 0x1) == 0){ \
131+
va_list vargs;
132+
133+
va_start(vargs, str);
134+
vprintf(str, vargs);
135+
va_end(vargs);
136+
}
137+
138+
return 0;
139+
}
140+
141+
131142
extern "C" {
132143
static const utest_v1_scheduler_t utest_v1_scheduler =
133144
{

frameworks/utest/source/stack_trace.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ void utest_dump_trace()
5151
{
5252
unsigned current = (trace_index == 0) ? UTEST_MAX_BACKTRACE - 1 : trace_index - 1;
5353

54-
printf("==================================================================\n");
55-
printf("Utest back trace: Total calls logged = %u.\n", total_calls);
56-
printf("==================================================================\n");
54+
utest_printf("==================================================================\n");
55+
utest_printf("Utest back trace: Total calls logged = %u.\n", total_calls);
56+
utest_printf("==================================================================\n");
5757
while (current != trace_index) {
5858

59-
printf("%u > %s\n", current, utest_trace[current].c_str());
59+
utest_printf("%u > %s\n", current, utest_trace[current].c_str());
6060
current = (current == 0) ? UTEST_MAX_BACKTRACE - 1 : current - 1;
6161
}
62-
printf("==================================================================\n");
62+
utest_printf("==================================================================\n");
6363
}
6464

6565
#endif

frameworks/utest/source/unity_handler.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,14 @@ void utest_unity_ignore_failure(void)
3232
UTEST_LOG_FUNCTION();
3333
utest::v1::Harness::raise_failure(utest::v1::failure_reason_t(utest::v1::REASON_ASSERTION | utest::v1::REASON_IGNORE));
3434
}
35+
36+
void utest_safe_putc(int chr)
37+
{
38+
volatile uint32_t primask = __get_PRIMASK();
39+
if ( (primask & 0x1) == 0){
40+
(void)putchar(chr);
41+
}
42+
43+
}
44+
45+

frameworks/utest/utest/shim.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ extern "C" {
7676
/// must be implemented by the port
7777
void utest_v1_enter_critical_section(void);
7878
void utest_v1_leave_critical_section(void);
79+
int utest_printf(char *str, ...);
7980

8081
/// This is the default scheduler implementation used by the harness.
8182
utest_v1_scheduler_t utest_v1_get_scheduler(void);

frameworks/utest/utest/unity_handler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#define UTEST_UNITY_ASSERT_FAILURE_H
2121

2222
#include <stdint.h>
23+
#include <stdio.h>
24+
#include "cmsis.h"
2325

2426
#ifdef __cplusplus
2527
extern "C" {
@@ -31,6 +33,8 @@ void utest_unity_assert_failure(void);
3133
/// this function is called from the unity module when an assertion failed, but is ignored.
3234
void utest_unity_ignore_failure(void);
3335

36+
void utest_safe_putc(int chr);
37+
3438
#ifdef __cplusplus
3539
}
3640
#endif

0 commit comments

Comments
 (0)