diff --git a/TESTS/mbedmicro-rtos-mbed/signals/main.cpp b/TESTS/mbedmicro-rtos-mbed/signals/main.cpp index 195bfcf2c2..0809b14f17 100644 --- a/TESTS/mbedmicro-rtos-mbed/signals/main.cpp +++ b/TESTS/mbedmicro-rtos-mbed/signals/main.cpp @@ -15,49 +15,394 @@ */ #include "mbed.h" #include "greentea-client/test_env.h" -#include "rtos.h" +#include "utest/utest.h" +#include "unity/unity.h" + +using utest::v1::Case; + #if defined(MBED_RTOS_SINGLE_THREAD) #error [NOT_SUPPORTED] test not supported #endif -#define TEST_STACK_SIZE 512 +#define TEST_STACK_SIZE 512 +#define MAX_FLAG_POS 30 -#define SIGNAL_SET_VALUE 0x01 -const int SIGNALS_TO_EMIT = 100; -const int SIGNAL_HANDLE_DELEY = 25; +#define ALL_SIGNALS 0x7fffffff +#define NO_SIGNALS 0x0 +#define PROHIBITED_SIGNAL 0x80000000 +#define SIGNAL1 0x1 +#define SIGNAL2 0x2 +#define SIGNAL3 0x4 -DigitalOut led(LED1); -int signal_counter = 0; +struct Sync { + Sync(Semaphore &parent, Semaphore &child): sem_parent(parent), sem_child(child) + {} -void led_thread() { - while (true) { - // Signal flags that are reported as event are automatically cleared. - Thread::signal_wait(SIGNAL_SET_VALUE); - led = !led; - signal_counter++; + Semaphore &sem_parent; + Semaphore &sem_child; +}; + + +/* In order to successfully run this test suite when compiled with --profile=debug + * error() has to be redefined as noop. + * + * ThreadFlags calls RTX API which uses Event Recorder functionality. When compiled + * with MBED_TRAP_ERRORS_ENABLED=1 (set in debug profile) EvrRtxEventFlagsError() calls error() + * which aborts test program. + */ +#if defined(MBED_TRAP_ERRORS_ENABLED) && MBED_TRAP_ERRORS_ENABLED +void error(const char* format, ...) { + (void) format; +} +#endif + + +template +void run_signal_wait(void) +{ + osEvent ev = Thread::signal_wait(signals, timeout); + TEST_ASSERT_EQUAL(test_val, ev.status); +} + +template +void run_release_signal_wait(Semaphore *sem) +{ + sem->release(); + osEvent ev = Thread::signal_wait(signals, timeout); + TEST_ASSERT_EQUAL(test_val, ev.status); +} + +template +void run_release_wait_signal_wait(Sync *sync) +{ + sync->sem_parent.release(); + sync->sem_child.wait(); + osEvent ev = Thread::signal_wait(signals, timeout); + TEST_ASSERT_EQUAL(test_val, ev.status); +} + +template +void run_clear(void) +{ + int32_t ret = Thread::signal_clr(signals); + TEST_ASSERT_EQUAL(test_val, ret); +} + +template +void run_wait_clear(Sync *sync) +{ + sync->sem_parent.release(); + sync->sem_child.wait(); + int32_t ret = Thread::signal_clr(signals); + TEST_ASSERT_EQUAL(test_val, ret); +} + +template +void run_double_wait_clear(Sync *sync) +{ + int32_t ret; + + sync->sem_parent.release(); + sync->sem_child.wait(); + ret = Thread::signal_clr(signals1); + TEST_ASSERT_EQUAL(test_val1, ret); + + ret = Thread::signal_clr(signals2); + TEST_ASSERT_EQUAL(test_val2, ret); +} + +void run_loop_wait_clear(Sync *sync) +{ + int32_t signals = NO_SIGNALS; + for (int i = 0; i <= MAX_FLAG_POS; i++) { + int32_t signal = 1 << i; + signals |= signal; + sync->sem_child.wait(); + int32_t ret = Thread::signal_clr(NO_SIGNALS); + TEST_ASSERT_EQUAL(signals, ret); + sync->sem_parent.release(); } } -int main (void) { - GREENTEA_SETUP(20, "default_auto"); - Thread thread(osPriorityNormal, TEST_STACK_SIZE); - thread.start(led_thread); +/** Validate that call signal_clr(NO_SIGNALS) doesn't change thread signals and return actual signals - bool result = false; + Given two threads A & B are started, B with all signals already set + When thread B calls @a signal_clr(NO_SIGNALS) + Then thread B @a signal_clr status should be ALL_SIGNALS indicating that thread B state is unchanged + */ +void test_clear_no_signals(void) +{ + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); - printf("Handling %d signals...\r\n", SIGNALS_TO_EMIT); - - while (true) { - Thread::wait(2 * SIGNAL_HANDLE_DELEY); - thread.signal_set(SIGNAL_SET_VALUE); - if (signal_counter == SIGNALS_TO_EMIT) { - printf("Handled %d signals\r\n", signal_counter); - result = true; - break; - } - } - GREENTEA_TESTSUITE_RESULT(result); - return 0; + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_double_wait_clear, &sync)); + sem_parent.wait(); + t.signal_set(ALL_SIGNALS); + sem_child.release(); + t.join(); +} + +/** Validate if any signals are set on just created thread + + Given the thread is running + When thread execute @a signal_clr(NO_SIGNALS) + Then thread @a signal_clr return status should be NO_SIGNALS(0) indicating no signals set + */ +void test_init_state(void) +{ + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_clear)); + t.join(); +} + +/** Validate all signals set in one shot + + Given two threads A & B are started + When thread A call @a signal_set(ALL_SIGNALS) with all possible signals + Then thread B @a signal_clr(NO_SIGNALS) status should be ALL_SIGNALS indicating all signals set correctly + */ +void test_set_all(void) +{ + int32_t ret; + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_wait_clear, &sync)); + + sem_parent.wait(); + ret = t.signal_set(ALL_SIGNALS); + TEST_ASSERT_EQUAL(ALL_SIGNALS, ret); + + sem_child.release(); + t.join(); +} + +/** Validate that call signal_set with prohibited signal doesn't change thread signals + + Given two threads A & B are started, B with all signals set + When thread A executes @a signal_set(PROHIBITED_SIGNAL) with prohibited signal + Then thread B @a signal_clr(NO_SIGNALS) status should be ALL_SIGNALS indicating that thread B signals are unchanged + + @note Each signal has up to 31 event flags 0x1, 0x2, 0x4, 0x8, ..., 0x40000000 + Most significant bit is reserved and thereby flag 0x80000000 is prohibited + */ +void test_set_prohibited(void) +{ + int32_t ret; + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_wait_clear, &sync)); + + sem_parent.wait(); + t.signal_set(ALL_SIGNALS); + + ret = t.signal_set(PROHIBITED_SIGNAL); + TEST_ASSERT_EQUAL(osErrorParameter, ret); + + sem_child.release(); + t.join(); +} + +/** Validate all signals clear in one shot + + Given two threads A & B are started, B with all signals set + When thread B execute @a signal_clr(ALL_SIGNALS) with all possible signals + Then thread B @a signal_clr(NO_SIGNALS) status should be NO_SIGNALS(0) indicating all signals cleared correctly + */ +void test_clear_all(void) +{ + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_double_wait_clear, &sync)); + sem_parent.wait(); + t.signal_set(ALL_SIGNALS); + sem_child.release(); + t.join(); +} + +/** Validate all signals set one by one in loop + + Given two threads A & B are started + When thread A executes @a signal_set(signal) in loop with all possible signals + */ +void test_set_all_loop(void) +{ + int32_t ret; + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_loop_wait_clear, &sync)); + + int32_t signals = 0; + for (int i = 0; i <= MAX_FLAG_POS; i++) { + int32_t signal = 1 << i; + + ret = t.signal_set(signal); + signals |= signal; + TEST_ASSERT_EQUAL(signals, ret); + sem_child.release(); + sem_parent.wait(); + } + t.join(); +} + +/** Validate signal_wait return status if timeout specified + + Given the thread is running + When thread executes @a signal_wait(signals, timeout) with specified signals and timeout + Then thread @a signal_wait status should be osEventTimeout indicating a timeout + thread @a signal_wait status should be osOK indicating 0[ms] timeout set + */ +template +void test_wait_timeout(void) +{ + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_signal_wait)); + t.join(); +} + +/** Validate that call of signal_wait return correctly when thread has all signals already set + + Given two threads A & B are started, B with all signals already set + When thread B executes @a signal_wait(ALL_SIGNALS, osWaitForever), + Then thread B @a signal_wait return immediately with status osEventSignal indicating all wait signals was already set + */ +void test_wait_all_already_set(void) +{ + Semaphore sem_parent(0, 1); + Semaphore sem_child(0, 1); + Sync sync(sem_parent, sem_child); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_wait_signal_wait, &sync)); + + sem_parent.wait(); + TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state()); + t.signal_set(ALL_SIGNALS); + sem_child.release(); + t.join(); +} + +/** Validate if signal_wait return correctly when all signals set + + Given two threads A & B are started and B waiting for a thread flag to be set + When thread A executes @a signal_set(ALL_SIGNALS) with all possible signals + Then thread B @a signal_wait status is osEventSignal indicating all wait signals was set + */ +void test_wait_all(void) +{ + Semaphore sem(0, 1); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_signal_wait, &sem)); + + sem.wait(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + t.signal_set(ALL_SIGNALS); + t.join(); +} + +/** Validate if signal_wait accumulate signals and return correctly when all signals set + + Given two threads A & B are started and B waiting for a thread signals to be set + When thread A executes @a signal_set setting all signals in loop + Then thread B @a signal_wait status is osEventSignal indicating that all wait signals was set + */ +void test_wait_all_loop(void) +{ + int32_t ret; + Semaphore sem(0, 1); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_signal_wait, &sem)); + + sem.wait(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + for (int i = 0; i < MAX_FLAG_POS; i++) { + int32_t signal = 1 << i; + ret = t.signal_set(signal); + } + ret = t.signal_set(1 << MAX_FLAG_POS); + TEST_ASSERT_EQUAL(NO_SIGNALS, ret); + t.join(); +} + +/** Validate if setting same signal twice cause any unwanted behaviour + + Given two threads A & B are started and B waiting for a thread signals to be set + When thread A executes @a signal_set twice for the same signal + Then thread A @a signal_set status is current signal set + thread B @a signal_wait status is osEventSignal indicating that all wait signals was set + */ +void test_set_double(void) +{ + int32_t ret; + Semaphore sem(0, 1); + + Thread t(osPriorityNormal, TEST_STACK_SIZE); + t.start(callback(run_release_signal_wait, &sem)); + + sem.wait(); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + ret = t.signal_set(SIGNAL1); + TEST_ASSERT_EQUAL(SIGNAL1, ret); + + ret = t.signal_set(SIGNAL2); + TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret); + + ret = t.signal_set(SIGNAL2); + TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret); + TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state()); + + ret = t.signal_set(SIGNAL3); + TEST_ASSERT_EQUAL(NO_SIGNALS, ret); + t.join(); +} + + +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(10, "default_auto"); + return utest::v1::verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Validate that call signal_clr(NO_SIGNALS) doesn't change thread signals and return actual signals", test_clear_no_signals), + Case("Validate if any signals are set on just created thread", test_init_state), + Case("Validate all signals set in one shot", test_set_all), + Case("Validate that call signal_set with prohibited signal doesn't change thread signals", test_set_prohibited), + Case("Validate all signals clear in one shot", test_clear_all), + Case("Validate all signals set one by one in loop", test_set_all_loop), + Case("Validate signal_wait return status if timeout specified: 0[ms] no signals", test_wait_timeout<0, 0, osOK>), + Case("Validate signal_wait return status if timeout specified: 0[ms] all signals", test_wait_timeout), + Case("Validate signal_wait return status if timeout specified: 1[ms] no signals", test_wait_timeout<0, 1, osEventTimeout>), + Case("Validate signal_wait return status if timeout specified: 1[ms] all signals", test_wait_timeout), + Case("Validate that call of signal_wait return correctly when thread has all signals already set", test_wait_all_already_set), + Case("Validate if signal_wait return correctly when all signals set", test_wait_all), + Case("Validate if signal_wait accumulate signals and return correctly when all signals set", test_wait_all_loop), + Case("Validate if setting same signal twice cause any unwanted behaviour", test_set_double) +}; + +utest::v1::Specification specification(test_setup, cases); + +int main() +{ + return !utest::v1::Harness::run(specification); }