Merge pull request #4729 from bulislaw/mutex_tests

RTOS: Mutex: Rework tests
pull/4786/head
Jimmy Brisson 2017-07-27 09:40:26 -05:00 committed by GitHub
commit c5c470e22c
2 changed files with 127 additions and 47 deletions

View File

@ -12,12 +12,8 @@ using namespace utest::v1;
#define TEST_STACK_SIZE 512 #define TEST_STACK_SIZE 512
#define TEST_ONE_SEC_MS (1000) #define TEST_LONG_DELAY 20
#define TEST_HALF_SEC_MS (500) #define TEST_DELAY 10
#define TEST_HALF_SEC_US (500000)
#define TEST_ONE_MS_US (1000)
#define THREAD_DELAY 50
#define SIGNALS_TO_EMIT 100 #define SIGNALS_TO_EMIT 100
Mutex stdio_mutex; Mutex stdio_mutex;
@ -26,11 +22,14 @@ volatile int change_counter = 0;
volatile bool changing_counter = false; volatile bool changing_counter = false;
volatile bool mutex_defect = false; volatile bool mutex_defect = false;
bool manipulate_protected_zone(const int thread_delay) { bool manipulate_protected_zone(const int thread_delay)
{
bool result = true; bool result = true;
osStatus stat = stdio_mutex.lock(); osStatus stat = stdio_mutex.lock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
core_util_critical_section_enter();
if (changing_counter == true) { if (changing_counter == true) {
result = false; result = false;
mutex_defect = true; mutex_defect = true;
@ -38,25 +37,37 @@ bool manipulate_protected_zone(const int thread_delay) {
changing_counter = true; changing_counter = true;
change_counter++; change_counter++;
core_util_critical_section_exit();
Thread::wait(thread_delay); Thread::wait(thread_delay);
core_util_critical_section_enter();
changing_counter = false; changing_counter = false;
core_util_critical_section_exit();
stat = stdio_mutex.unlock(); stat = stdio_mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
return result; return result;
} }
void test_thread(int const *thread_delay) { void test_thread(int const *thread_delay)
{
while (true) { while (true) {
manipulate_protected_zone(*thread_delay); manipulate_protected_zone(*thread_delay);
} }
} }
/** Test multiple thread
Given 3 threads started with different delays and a section protected with a mutex
when each thread runs it tries to lock the mutex
then no more than one thread should be able to access protected region
*/
void test_multiple_threads(void) void test_multiple_threads(void)
{ {
const int t1_delay = THREAD_DELAY * 1; const int t1_delay = TEST_DELAY * 1;
const int t2_delay = THREAD_DELAY * 2; const int t2_delay = TEST_DELAY * 2;
const int t3_delay = THREAD_DELAY * 3; const int t3_delay = TEST_DELAY * 3;
Thread t2(osPriorityNormal, TEST_STACK_SIZE); Thread t2(osPriorityNormal, TEST_STACK_SIZE);
Thread t3(osPriorityNormal, TEST_STACK_SIZE); Thread t3(osPriorityNormal, TEST_STACK_SIZE);
@ -69,34 +80,51 @@ void test_multiple_threads(void)
Thread::wait(t1_delay); Thread::wait(t1_delay);
manipulate_protected_zone(t1_delay); manipulate_protected_zone(t1_delay);
core_util_critical_section_enter();
if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) { if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) {
core_util_critical_section_exit();
t2.terminate(); t2.terminate();
t3.terminate(); t3.terminate();
break; break;
} }
core_util_critical_section_exit();
} }
TEST_ASSERT_EQUAL(mutex_defect, false); TEST_ASSERT_EQUAL(false, mutex_defect);
} }
void test_dual_thread_nolock_lock_thread(Mutex *mutex) void test_dual_thread_nolock_lock_thread(Mutex *mutex)
{ {
bool stat_b = mutex->trylock(); osStatus stat = mutex->lock(osWaitForever);
TEST_ASSERT_EQUAL(stat_b, true); TEST_ASSERT_EQUAL(osOK, stat);
osStatus stat = mutex->unlock(); stat = mutex->unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
void test_dual_thread_nolock_trylock_thread(Mutex *mutex) void test_dual_thread_nolock_trylock_thread(Mutex *mutex)
{ {
bool stat_b = mutex->trylock(); bool stat_b = mutex->trylock();
TEST_ASSERT_EQUAL(stat_b, true); TEST_ASSERT_EQUAL(true, stat_b);
osStatus stat = mutex->unlock(); osStatus stat = mutex->unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
/** Test dual thread no-lock
Test dual thread second thread lock
Given two threads A & B and a mutex
When thread A creates a mutex and starts thread B
and thread B calls @a lock and @a unlock
Then returned statuses are osOK
Test dual thread second thread trylock
Given two threads A & B and a mutex
When thread A creates a mutex and starts thread B
and thread B calls @a trylock and @a unlock
Then returned statuses are true and osOK
*/
template <void (*F)(Mutex *)> template <void (*F)(Mutex *)>
void test_dual_thread_nolock(void) void test_dual_thread_nolock(void)
{ {
@ -105,15 +133,24 @@ void test_dual_thread_nolock(void)
thread.start(callback(F, &mutex)); thread.start(callback(F, &mutex));
wait_us(TEST_HALF_SEC_MS); wait_ms(TEST_DELAY);
} }
void test_dual_thread_lock_unlock_thread(Mutex *mutex) void test_dual_thread_lock_unlock_thread(Mutex *mutex)
{ {
osStatus stat = mutex->lock(osWaitForever); osStatus stat = mutex->lock(osWaitForever);
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
/** Test dual thread lock unlock
Given two threads and a lock
When thread A locks the lock and starts thread B
and thread B calls @a lock on the mutex
Then thread B waits for thread A to unlock the lock
When thread A calls @a unlock on the mutex
Then thread B acquires the lock
*/
void test_dual_thread_lock_unlock(void) void test_dual_thread_lock_unlock(void)
{ {
Mutex mutex; Mutex mutex;
@ -121,31 +158,45 @@ void test_dual_thread_lock_unlock(void)
Thread thread(osPriorityNormal, TEST_STACK_SIZE); Thread thread(osPriorityNormal, TEST_STACK_SIZE);
stat = mutex.lock(); stat = mutex.lock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex)); thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex));
stat = mutex.unlock(); stat = mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
wait_us(TEST_HALF_SEC_MS); wait_ms(TEST_DELAY);
} }
void test_dual_thread_lock_trylock_thread(Mutex *mutex) void test_dual_thread_lock_trylock_thread(Mutex *mutex)
{ {
bool stat = mutex->trylock(); bool stat = mutex->trylock();
TEST_ASSERT_EQUAL(stat, false); TEST_ASSERT_EQUAL(false, stat);
} }
void test_dual_thread_lock_lock_thread(Mutex *mutex) void test_dual_thread_lock_lock_thread(Mutex *mutex)
{ {
uint32_t start = us_ticker_read(); uint32_t start = us_ticker_read();
osStatus stat = mutex->lock(TEST_HALF_SEC_MS); osStatus stat = mutex->lock(TEST_DELAY);
TEST_ASSERT_EQUAL(stat, osErrorTimeout); TEST_ASSERT_EQUAL(osErrorTimeout, stat);
TEST_ASSERT_UINT32_WITHIN(TEST_ONE_MS_US, TEST_HALF_SEC_US, us_ticker_read() - start); TEST_ASSERT_UINT32_WITHIN(5000, TEST_DELAY*1000, us_ticker_read() - start);
} }
/** Test dual thread lock
Test dual thread lock locked
Given a mutex and two threads A & B
When thread A calls @a lock and starts thread B
and thread B calls @a lock with 500ms timeout
Then thread B waits 500ms and timeouts
Test dual thread trylock locked
Given a mutex and two threads A & B
When thread A calls @a lock and starts thread B
Then thread B calls @a trylock
and thread B fails to acquire the lock
*/
template <void (*F)(Mutex *)> template <void (*F)(Mutex *)>
void test_dual_thread_lock(void) void test_dual_thread_lock(void)
{ {
@ -154,59 +205,78 @@ void test_dual_thread_lock(void)
Thread thread(osPriorityNormal, TEST_STACK_SIZE); Thread thread(osPriorityNormal, TEST_STACK_SIZE);
stat = mutex.lock(); stat = mutex.lock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
thread.start(callback(F, &mutex)); thread.start(callback(F, &mutex));
wait_us(TEST_ONE_SEC_MS); wait_ms(TEST_LONG_DELAY);
stat = mutex.unlock(); stat = mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
/** Test single thread lock recursive
Given a mutex and a single running thread
When thread calls @a lock twice and @a unlock twice on the mutex
Then the returned statuses are osOK
*/
void test_single_thread_lock_recursive(void) void test_single_thread_lock_recursive(void)
{ {
Mutex mutex; Mutex mutex;
osStatus stat; osStatus stat;
stat = mutex.lock(); stat = mutex.lock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
stat = mutex.lock(); stat = mutex.lock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
stat = mutex.unlock(); stat = mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
stat = mutex.unlock(); stat = mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
/** Test single thread trylock
Given a mutex and a single running thread
When thread calls @a trylock and @a unlock on the mutex
Then the returned statuses are osOK
*/
void test_single_thread_trylock(void) void test_single_thread_trylock(void)
{ {
Mutex mutex; Mutex mutex;
bool stat_b = mutex.trylock(); bool stat_b = mutex.trylock();
TEST_ASSERT_EQUAL(stat_b, true); TEST_ASSERT_EQUAL(true, stat_b);
osStatus stat = mutex.unlock(); osStatus stat = mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
/** Test single thread lock
Given a mutex and a single running thread
When thread calls @a lock and @a unlock on the mutex
Then the returned statuses are osOK
*/
void test_single_thread_lock(void) void test_single_thread_lock(void)
{ {
Mutex mutex; Mutex mutex;
osStatus stat; osStatus stat;
stat = mutex.lock(); stat = mutex.lock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
stat = mutex.unlock(); stat = mutex.unlock();
TEST_ASSERT_EQUAL(stat, osOK); TEST_ASSERT_EQUAL(osOK, stat);
} }
utest::v1::status_t test_setup(const size_t number_of_cases) { utest::v1::status_t test_setup(const size_t number_of_cases)
GREENTEA_SETUP(15, "default_auto"); {
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases); return verbose_test_setup_handler(number_of_cases);
} }
@ -224,6 +294,7 @@ Case cases[] = {
Specification specification(test_setup, cases); Specification specification(test_setup, cases);
int main() { int main()
{
return !Harness::run(specification); return !Harness::run(specification);
} }

View File

@ -53,7 +53,12 @@ public:
/** Wait until a Mutex becomes available. /** Wait until a Mutex becomes available.
@param millisec timeout value or 0 in case of no time-out. (default: osWaitForever) @param millisec timeout value or 0 in case of no time-out. (default: osWaitForever)
@return status code that indicates the execution status of the function. @return status code that indicates the execution status of the function:
@a osOK the mutex has been obtained.
@a osErrorTimeout the mutex could not be obtained in the given time.
@a osErrorParameter internal error.
@a osErrorResource the mutex could not be obtained when no timeout was specified.
@a osErrorISR this function cannot be called from the interrupt service routine.
*/ */
osStatus lock(uint32_t millisec=osWaitForever); osStatus lock(uint32_t millisec=osWaitForever);
@ -63,7 +68,11 @@ public:
bool trylock(); bool trylock();
/** Unlock the mutex that has previously been locked by the same thread /** Unlock the mutex that has previously been locked by the same thread
@return status code that indicates the execution status of the function. @return status code that indicates the execution status of the function:
@a osOK the mutex has been released.
@a osErrorParameter internal error.
@a osErrorResource the mutex was not locked or the current thread wasn't the owner.
@a osErrorISR this function cannot be called from the interrupt service routine.
*/ */
osStatus unlock(); osStatus unlock();