Import Mbed OS hard-float snapshot

This commit is contained in:
Beslan
2026-06-01 20:15:04 +03:00
commit d3738e2f89
16278 changed files with 10628036 additions and 0 deletions

View File

@@ -0,0 +1,688 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#if !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] MemoryPool test cases require a RTOS to run.
#else
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
#define TEST_ASSERT_TIME_POINT_WITHIN(delta, expected, actual) \
do { \
using ct_tp = std::common_type_t<decltype(expected), decltype(actual)>; \
using ct = std::common_type_t<decltype(delta), ct_tp::duration>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected.time_since_epoch()).count(), ct(actual.time_since_epoch()).count()); \
} while (0)
using namespace utest::v1;
using namespace std::chrono;
#define THREAD_STACK_SIZE 512
#define TEST_TIMEOUT 50ms
/* Enum used to select block allocation method. */
typedef enum {
ALLOC, CALLOC
} AllocType;
/* Structure for complex block type. */
typedef struct {
int a;
char b;
int c;
} COMPLEX_TYPE;
/* Function to check if complex type object is cleared.*/
bool comp_is_cleared(COMPLEX_TYPE *object)
{
if (object->a == 0 && object->b == 0 && object->c == 0) {
return true;
}
return false;
}
/* Function to check if complex type object holds specified values.*/
bool comp_is_equal(COMPLEX_TYPE *object, int a, char b, int c)
{
if (object->a == a && object->b == b && object->c == c) {
return true;
}
return false;
}
/* Function to set complex type object fields.*/
void comp_set(COMPLEX_TYPE *object, int a, char b, int c)
{
object->a = a;
object->b = b;
object->c = c;
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Given MemoryPool object of the specified type and queue size has
* been successfully created.
* When max number of blocks is allocated from the pool.
* Then all allocations are successful.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_alloc_success(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
/* Test alloc()/calloc() methods - try to allocate max number of
blocks. All allocations should be successful. */
for (i = 0; i < numOfEntries; i++) {
/* Allocate memory block. */
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
/* Check if Calloc clears the block. */
if (atype == CALLOC) {
TEST_ASSERT_EQUAL(0, *p_blocks[i]);
}
/* Init fields. */
*p_blocks[i] = (i + 5);
}
/* Check if blocks holds valid values. */
for (i = 0; i < numOfEntries; i++) {
TEST_ASSERT_EQUAL((i + 5), *p_blocks[i]);
}
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Complex memory pool block type is used.
*
* Given MemoryPool object of the specified type and queue size has
* been successfully created.
* When max number of blocks is allocated from the pool.
* Then all allocations are successful.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_alloc_success_complex(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
/* Test alloc()/calloc() methods - try to allocate max number of
blocks. All allocations should be successful. */
for (i = 0; i < numOfEntries; i++) {
/* Allocate memory block. */
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
/* Check if Calloc clears the block. */
if (atype == CALLOC) {
TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[i]));
}
/* Init fields. */
comp_set(p_blocks[i], i + 1, i + 2, i + 3);
}
/* Check if blocks holds valid values. */
for (i = 0; i < numOfEntries; i++) {
TEST_ASSERT_EQUAL(true, comp_is_equal(p_blocks[i], i + 1, i + 2, i + 3));
}
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Given MemoryPool has already max number of blocks allocated from the pool.
* When next block is allocated.
* Then allocation fails.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_alloc_fail(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
T *p_extra_block;
uint32_t i;
/* Allocate all available blocks. */
for (i = 0; i < numOfEntries; i++) {
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
}
/* There are no more blocks available. Try to allocate another block. */
if (atype == ALLOC) {
p_extra_block = mem_pool.try_alloc();
} else {
p_extra_block = mem_pool.try_calloc();
}
/* Show that memory pool block has NOT been allocated. */
TEST_ASSERT_NULL(p_extra_block);
}
/* Template for functional tests for free() function
* of MemoryPool object.
*
* Given MemoryPool has all blocks allocated.
* When free operation is executed on the each allocated block.
* Then each deallocation is successfully performed.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_success(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
osStatus status;
/* Allocate all available blocks. */
for (i = 0; i < numOfEntries; i++) {
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
}
/* Free all memory blocks. */
for (i = 0; i < numOfEntries; i++) {
status = mem_pool.free(p_blocks[i]);
/* Check operation status. */
TEST_ASSERT_EQUAL(osOK, status);
}
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Basic memory pool block type is used.
*
* Given MemoryPool had all blocks allocated and one block has
* been freed (last).
* When next block is allocated.
* Then allocation is successful.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_last(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
osStatus status;
/* Allocate all available blocks. */
for (i = 0; i < numOfEntries; i++) {
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Init block. */
*p_blocks[i] = 0xAB;
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
}
/* Free the last block. */
status = mem_pool.free(p_blocks[numOfEntries - 1]);
/* Check status. */
TEST_ASSERT_EQUAL(osOK, status);
/* Try to allocate another block (one block is now available). */
if (atype == ALLOC) {
p_blocks[numOfEntries - 1] = mem_pool.try_alloc();
} else {
p_blocks[numOfEntries - 1] = mem_pool.try_calloc();
}
/* Show that memory pool block has been now allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[numOfEntries - 1]);
/* Check if Calloc clears the block. */
if (atype == CALLOC) {
TEST_ASSERT_EQUAL(0, *p_blocks[numOfEntries - 1]);
}
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Complex memory pool block type is used.
*
* Given MemoryPool had all blocks allocated and one block has
* been freed (last).
* When next block is allocated.
* Then allocation is successful.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_last_complex(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
osStatus status;
/* Allocate all available blocks. */
for (i = 0; i < numOfEntries; i++) {
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Init block. */
comp_set(p_blocks[i], i + 1, i + 2, i + 3);
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
}
/* Free the last block. */
status = mem_pool.free(p_blocks[numOfEntries - 1]);
/* Check status. */
TEST_ASSERT_EQUAL(osOK, status);
/* Try to allocate another block (one block is now available). */
if (atype == ALLOC) {
p_blocks[numOfEntries - 1] = mem_pool.try_alloc();
} else {
p_blocks[numOfEntries - 1] = mem_pool.try_calloc();
}
/* Show that memory pool block has been now allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[numOfEntries - 1]);
/* Check if Calloc clears the block. */
if (atype == CALLOC) {
TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[numOfEntries - 1]));
}
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Basic memory pool block type is used.
*
* Given MemoryPool had all blocks allocated and one block has
* been freed (first).
* When next block is allocated.
* Then allocation is successful.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_first(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
osStatus status;
/* Allocate all available blocks. */
for (i = 0; i < numOfEntries; i++) {
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Init block. */
*p_blocks[i] = 0xAB;
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
}
/* Free the last block. */
status = mem_pool.free(p_blocks[0]);
/* Check status. */
TEST_ASSERT_EQUAL(osOK, status);
/* Try to allocate another block (one block is now available). */
if (atype == ALLOC) {
p_blocks[0] = mem_pool.try_alloc();
} else {
p_blocks[0] = mem_pool.try_calloc();
}
/* Show that memory pool block has been now allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[0]);
/* Check if Calloc clears the block. */
if (atype == CALLOC) {
TEST_ASSERT_EQUAL(0, *p_blocks[0]);
}
}
/* Template for functional tests for try_alloc(), try_calloc() functions
* of MemoryPool object.
*
* Complex memory pool block type is used.
*
* Given MemoryPool had all blocks allocated and one block has
* been freed (first).
* When next block is allocated.
* Then allocation is successful.
*
* */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_first_complex(AllocType atype)
{
MemoryPool<T, numOfEntries> mem_pool;
T *p_blocks[numOfEntries];
uint32_t i;
osStatus status;
/* Allocate all available blocks. */
for (i = 0; i < numOfEntries; i++) {
if (atype == ALLOC) {
p_blocks[i] = mem_pool.try_alloc();
} else {
p_blocks[i] = mem_pool.try_calloc();
}
/* Init block. */
comp_set(p_blocks[i], i + 1, i + 2, i + 3);
/* Show that memory pool block has been allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[i]);
}
/* Free the last block. */
status = mem_pool.free(p_blocks[0]);
/* Check status. */
TEST_ASSERT_EQUAL(osOK, status);
/* Try to allocate another block (one block is now available). */
if (atype == ALLOC) {
p_blocks[0] = mem_pool.try_alloc();
} else {
p_blocks[0] = mem_pool.try_calloc();
}
/* Show that memory pool block has been now allocated. */
TEST_ASSERT_NOT_NULL(p_blocks[0]);
/* Check if Calloc clears the block. */
if (atype == CALLOC) {
TEST_ASSERT_EQUAL(true, comp_is_cleared(p_blocks[0]));
}
}
/* Test try_alloc_for/try_alloc_until timeout
*
* Given a pool with one slot for int data
* When a thread tries to allocate two blocks with @ TEST_TIMEOUT timeout
* Then first operation succeeds immediately and second fails at the correct time.
*/
void test_mem_pool_timeout()
{
MemoryPool<int, 1> mem_pool;
Timer timer;
timer.start();
int *item = mem_pool.try_alloc_for(TEST_TIMEOUT);
TEST_ASSERT_NOT_NULL(item);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, 0ms, timer.elapsed_time());
item = mem_pool.try_alloc_for(TEST_TIMEOUT);
TEST_ASSERT_NULL(item);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time());
auto end_time = Kernel::Clock::now() + TEST_TIMEOUT;
item = mem_pool.try_alloc_until(end_time);
TEST_ASSERT_NULL(item);
TEST_ASSERT_TIME_POINT_WITHIN(TEST_TIMEOUT / 10, end_time, Kernel::Clock::now());
}
namespace {
struct free_capture {
MemoryPool<int, 1> *pool;
int *item;
};
}
static void free_int_item(free_capture *to_free)
{
ThisThread::sleep_for(TEST_TIMEOUT);
osStatus status = to_free->pool->free(to_free->item);
TEST_ASSERT_EQUAL(osOK, status);
}
/** Test alloc wait forever
*
* Given two threads A & B and a pool with one slot for int data
* When thread A allocs a block from the pool and tries to alloc a second one with @a osWaitForever timeout
* Then thread waits for a block to become free in the pool
* When thread B frees the first block from the pool
* Then thread A successfully allocs a block from the pool
*/
void test_mem_pool_waitforever()
{
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
MemoryPool<int, 1> pool;
Timer timer;
timer.start();
int *item = pool.try_alloc_for(Kernel::wait_for_u32_forever);
TEST_ASSERT_NOT_NULL(item);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, 0ms, timer.elapsed_time());
struct free_capture to_free;
to_free.pool = &pool;
to_free.item = item;
t.start(callback(free_int_item, &to_free));
item = pool.try_alloc_for(Kernel::wait_for_u32_forever);
TEST_ASSERT_EQUAL(item, to_free.item);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time());
t.join();
}
/* Robustness checks for free() function.
* Function under test is called with invalid parameters.
*
* Given MemoryPool object has been successfully created.
* When free operation is performed on NULL address.
* Then deallocation fails with osErrorParameter error.
*
*/
void free_block_invalid_parameter_null()
{
MemoryPool<int, 1> mem_pool;
osStatus status;
/* Try to free block passing invalid parameter (NULL). */
status = mem_pool.free(NULL);
/* Check operation status. */
TEST_ASSERT_EQUAL(osErrorParameter, status);
}
/* Robustness checks for free() function.
* Function under test is called with invalid parameters.
*
* Given MemoryPool object has been successfully created.
* When free operation is performed on invalid address.
* Then deallocation fails with osErrorParameter error.
*
*/
void free_block_invalid_parameter()
{
MemoryPool<int, 1> mem_pool;
osStatus status;
/* Try to free block passing invalid parameter (variable address). */
status = mem_pool.free(reinterpret_cast<int *>(&status));
/* Check operation status. */
TEST_ASSERT_EQUAL(osErrorParameter, status);
}
/* Use wrapper functions to reduce memory usage. */
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_alloc_success_wrapper()
{
test_mem_pool_alloc_success<T, numOfEntries>(ALLOC);
test_mem_pool_alloc_success<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_alloc_success_complex_wrapper()
{
test_mem_pool_alloc_success_complex<T, numOfEntries>(ALLOC);
test_mem_pool_alloc_success_complex<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_success_wrapper()
{
test_mem_pool_free_success<T, numOfEntries>(ALLOC);
test_mem_pool_free_success<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_last_wrapper()
{
test_mem_pool_free_realloc_last<T, numOfEntries>(ALLOC);
test_mem_pool_free_realloc_last<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_first_wrapper()
{
test_mem_pool_free_realloc_first<T, numOfEntries>(ALLOC);
test_mem_pool_free_realloc_first<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_first_complex_wrapper()
{
test_mem_pool_free_realloc_first_complex<T, numOfEntries>(ALLOC);
test_mem_pool_free_realloc_first_complex<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_free_realloc_last_complex_wrapper()
{
test_mem_pool_free_realloc_last_complex<T, numOfEntries>(ALLOC);
test_mem_pool_free_realloc_last_complex<T, numOfEntries>(CALLOC);
}
template<typename T, const uint32_t numOfEntries>
void test_mem_pool_alloc_fail_wrapper()
{
test_mem_pool_alloc_fail<T, numOfEntries>(ALLOC);
test_mem_pool_alloc_fail<T, numOfEntries>(CALLOC);
}
Case cases[] = {
Case("Test: try_alloc()/try_calloc() - success, 4 bytes b_type, q_size equal to 1.", test_mem_pool_alloc_success_wrapper<int, 1>),
Case("Test: try_alloc()/try_calloc() - success, 4 bytes b_type, q_size equal to 3.", test_mem_pool_alloc_success_wrapper<int, 3>),
Case("Test: try_alloc()/try_calloc() - success, 1 bytes b_type, q_size equal to 1.", test_mem_pool_alloc_success_wrapper<char, 1>),
Case("Test: try_alloc()/try_calloc() - success, 1 bytes b_type, q_size equal to 3.", test_mem_pool_alloc_success_wrapper<char, 3>),
Case("Test: try_alloc()/try_calloc() - success, complex b_type, q_size equal to 1.", test_mem_pool_alloc_success_complex_wrapper<COMPLEX_TYPE, 1>),
Case("Test: try_alloc()/try_calloc() - success, complex b_type, q_size equal to 3.", test_mem_pool_alloc_success_complex_wrapper<COMPLEX_TYPE, 3>),
Case("Test: free() - success, 4 bytes b_type, q_size equal to 1.", test_mem_pool_free_success_wrapper<int, 1>),
Case("Test: free() - success, 4 bytes b_type, q_size equal to 3.", test_mem_pool_free_success_wrapper<int, 3>),
Case("Test: free() - success, complex b_type, q_size equal to 1.", test_mem_pool_free_success_wrapper<COMPLEX_TYPE, 1>),
Case("Test: free() - success, complex b_type, q_size equal to 3.", test_mem_pool_free_success_wrapper<COMPLEX_TYPE, 3>),
Case("Test: re-allocation of the last block, basic type.", test_mem_pool_free_realloc_last_wrapper<int, 3>),
Case("Test: re-allocation of the first block, basic type.", test_mem_pool_free_realloc_first_wrapper<int, 3>),
Case("Test: re-allocation of the first block, complex type.", test_mem_pool_free_realloc_first_complex_wrapper<COMPLEX_TYPE, 3>),
Case("Test: re-allocation of the last block, complex type.", test_mem_pool_free_realloc_last_complex_wrapper<COMPLEX_TYPE, 3>),
Case("Test: fail (out of free blocks).", test_mem_pool_alloc_fail_wrapper<int, 3>),
Case("Test: timeout", test_mem_pool_timeout),
Case("Test: wait forever", test_mem_pool_waitforever),
Case("Test: free() - robust (free called with invalid param - NULL).", free_block_invalid_parameter_null),
Case("Test: free() - robust (free called with invalid param).", free_block_invalid_parameter)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(20, "default_auto");
return greentea_test_setup_handler(number_of_cases);
}
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
int main()
{
Harness::run(specification);
}
#endif // !defined(MBED_CONF_RTOS_PRESENT)

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2013-2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] RTOS basic test cases require RTOS with multithread to run
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#if defined(SKIP_TIME_DRIFT_TESTS)
#error [NOT_SUPPORTED] timing accuracy tests skipped
#endif // defined(SKIP_TIME_DRIFT_TESTS
using utest::v1::Case;
using std::milli;
using std::micro;
using namespace std::chrono;
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
#define TEST_STACK_SIZE 512
#else
#define TEST_STACK_SIZE 256
#endif
static duration<uint32_t, milli> elapsed_time_ms;
static const int test_timeout = 40;
void update_tick_thread(Mutex *mutex)
{
while (true) {
ThisThread::sleep_for(1ms);
mutex->lock();
++elapsed_time_ms;
mutex->unlock();
}
}
/** Tests is to measure the accuracy of ThisThread::sleep_for() over a period of time
Given
a thread updating elapsed_time_ms every milli sec
and host script for time measurement accuracy check (More details on tests can be found in timing_drift_auto.py)
When host query what is current count base_time
Then Device responds by the elapsed_time_ms
When host query what is current count final_time
Then Device responds by the elapsed_time_ms
When host computes the drift considering base_time, final_time, transport delay and measurement stretch
Then host send the results back to device pass/fail based on tolerance
*/
void test(void)
{
char _key[11] = { };
char _value[128] = { };
int expected_key = 1;
Mutex mutex;
duration<uint32_t, micro> elapsed_time;
Thread tick_thread(osPriorityHigh, TEST_STACK_SIZE);
tick_thread.start(callback(update_tick_thread, &mutex));
greentea_send_kv("timing_drift_check_start", 0);
// wait for 1st signal from host
do {
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
expected_key = strcmp(_key, "base_time");
} while (expected_key);
mutex.lock();
elapsed_time = elapsed_time_ms;
mutex.unlock();
// send base_time
greentea_send_kv(_key, elapsed_time.count());
// wait for 2nd signal from host
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
mutex.lock();
elapsed_time = elapsed_time_ms;
mutex.unlock();
// send final_time
greentea_send_kv(_key, elapsed_time.count());
//get the results from host
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING_MESSAGE("pass", _key, "Host side script reported a fail...");
}
Case cases[] = {
Case("Test ThisThread::sleep_for accuracy", test)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(test_timeout, "timing_drift_auto");
return utest::v1::greentea_test_setup_handler(number_of_cases);
}
utest::v1::Specification specification(greentea_test_setup, cases);
int main()
{
utest::v1::Harness::run(specification);
}
#endif // !DEVICE_USTICKER
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)

View File

@@ -0,0 +1,195 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] Condition variable test cases require RTOS with multithread to run
#else
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
using namespace utest::v1;
using namespace std::chrono_literals;
#define TEST_STACK_SIZE 512
#define TEST_DELAY 10ms
static int change_counter = 0;
static Mutex mutex;
static ConditionVariable cond(mutex);
void increment_on_signal()
{
mutex.lock();
cond.wait();
change_counter++;
mutex.unlock();
}
void test_notify_one()
{
Thread t1(osPriorityNormal, TEST_STACK_SIZE);
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
change_counter = 0;
t1.start(increment_on_signal);
t2.start(increment_on_signal);
ThisThread::sleep_for(TEST_DELAY);
TEST_ASSERT_EQUAL(0, change_counter);
mutex.lock();
cond.notify_one();
mutex.unlock();
ThisThread::sleep_for(TEST_DELAY);
TEST_ASSERT_EQUAL(1, change_counter);
mutex.lock();
cond.notify_one();
mutex.unlock();
t1.join();
t2.join();
}
void test_notify_all()
{
Thread t1(osPriorityNormal, TEST_STACK_SIZE);
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
change_counter = 0;
t1.start(increment_on_signal);
t2.start(increment_on_signal);
ThisThread::sleep_for(TEST_DELAY);
TEST_ASSERT_EQUAL(0, change_counter);
mutex.lock();
cond.notify_all();
mutex.unlock();
ThisThread::sleep_for(TEST_DELAY);
TEST_ASSERT_EQUAL(2, change_counter);
t1.join();
t2.join();
}
class TestConditionVariable : public ConditionVariable {
public:
static void test_linked_list(void)
{
Waiter *list = NULL;
Waiter w1;
Waiter w2;
Waiter w3;
Waiter w4;
TEST_ASSERT_EQUAL(0, validate_and_get_size(&list));
// Add 4 nodes
_add_wait_list(&list, &w1);
TEST_ASSERT_EQUAL(1, validate_and_get_size(&list));
_add_wait_list(&list, &w2);
TEST_ASSERT_EQUAL(2, validate_and_get_size(&list));
_add_wait_list(&list, &w3);
TEST_ASSERT_EQUAL(3, validate_and_get_size(&list));
_add_wait_list(&list, &w4);
TEST_ASSERT_EQUAL(4, validate_and_get_size(&list));
// Remove a middle node
_remove_wait_list(&list, &w2);
TEST_ASSERT_EQUAL(3, validate_and_get_size(&list));
// Remove front node
_remove_wait_list(&list, &w1);
TEST_ASSERT_EQUAL(2, validate_and_get_size(&list));
// remove back node
_remove_wait_list(&list, &w4);
TEST_ASSERT_EQUAL(1, validate_and_get_size(&list));
// remove last node
_remove_wait_list(&list, &w3);
TEST_ASSERT_EQUAL(0, validate_and_get_size(&list));
TEST_ASSERT_EQUAL_PTR(NULL, list);
}
/**
* Validate the linked list an return the number of elements
*
* If this list is invalid then this function asserts and does not
* return.
*
* Every node in a valid linked list has the properties:
* 1. node->prev->next == node
* 2. node->next->prev == node
*/
static int validate_and_get_size(Waiter **list)
{
Waiter *first = *list;
if (NULL == first) {
// List is empty
return 0;
}
int size = 0;
Waiter *current = first;
do {
TEST_ASSERT_EQUAL_PTR(current, current->prev->next);
TEST_ASSERT_EQUAL_PTR(current, current->next->prev);
current = current->next;
size++;
} while (current != first);
return size;
}
};
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Test notify one", test_notify_one),
Case("Test notify all", test_notify_all),
Case("Test linked list", TestConditionVariable::test_linked_list),
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}
#endif // !DEVICE_USTICKER
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)

View File

@@ -0,0 +1,452 @@
/*
* Copyright (c) 2013-2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity/unity.h"
#include "utest/utest.h"
using utest::v1::Case;
using namespace std::chrono;
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#if defined(MBED_CONF_RTOS_PRESENT)
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
#define THREAD_STACK_SIZE 512
#else
#define THREAD_STACK_SIZE 320 /* 512B stack on GCC_ARM compiler cause out of memory on some 16kB RAM boards e.g. NUCLEO_F070RB */
#endif
#endif
#define MAX_FLAG_POS 30
#define PROHIBITED_FLAG_POS 31
/* flags */
#define FLAG01 0x1FFF /* 00000000000000000001111111111111 */
#define FLAG02 0x3FFE000 /* 00000011111111111110000000000000 */
#define FLAG03 0x7C000000 /* 01111100000000000000000000000000 */
#define PROHIBITED_FLAG 0x80000000 /* 10000000000000000000000000000000 */
#define NO_FLAGS 0x0
void send_thread(EventFlags *ef, uint32_t flags, milliseconds wait)
{
for (uint32_t i = 0; i <= MAX_FLAG_POS; i++) {
const uint32_t flag = flags & (1 << i);
if (flag) {
ef->set(flag);
if (wait != 0ms) {
ThisThread::sleep_for(wait);
}
}
}
}
#if defined(MBED_CONF_RTOS_PRESENT)
Semaphore sync_sem(0, 1);
void send_thread_sync(EventFlags *ef, uint32_t flags, milliseconds wait)
{
for (uint32_t i = 0; i <= MAX_FLAG_POS; i++) {
const uint32_t flag = flags & (1 << i);
if (flag) {
sync_sem.acquire();
ef->set(flag);
ThisThread::sleep_for(wait);
}
}
}
void wait_thread_all(EventFlags *ef, uint32_t flags)
{
uint32_t ret, flags_after_clear;
ret = ef->wait_all(flags);
flags_after_clear = ef->get();
TEST_ASSERT(flags | ret);
TEST_ASSERT(flags | ~flags_after_clear);
}
#endif
/** Test if get on empty EventFlags object return NO_FLAGS
Given a empty EventFlags object
When call @a get
Then @a get return status is NO_FLAGS
*/
void test_empty_get(void)
{
EventFlags ev;
uint32_t flags;
flags = ev.get();
TEST_ASSERT_EQUAL(NO_FLAGS, flags);
}
/** Test if clear on empty EventFlags object return NO_FLAGS
Given a empty EventFlags object
When call @a clear(NO_FLAGS)
Then @a clear return status is NO_FLAGS
*/
void test_empty_clear(void)
{
EventFlags ev;
uint32_t flags;
flags = ev.clear(NO_FLAGS);
TEST_ASSERT_EQUAL(NO_FLAGS, flags);
}
/** Test if set on empty EventFlags object return NO_FLAGS
Given a empty EventFlags object
When call @a set(NO_FLAGS)
Then @a set return status is NO_FLAGS
*/
void test_empty_set(void)
{
EventFlags ev;
uint32_t flags;
flags = ev.set(NO_FLAGS);
TEST_ASSERT_EQUAL(NO_FLAGS, flags);
}
/** Test if call of set/clean with PROHIBITED_FLAG doesn't invalidates object flags
Given a EventFlags object with all flags already set
When call @a clear(PROHIBITED_FLAG) with prohibited flag
Then @a clear return status is osFlagsErrorParameter and object flags stays unchanged
When call @a set(PROHIBITED_FLAG) with prohibited flag
Then @a set return status is osFlagsErrorParameter and object flags stays 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_prohibited(void)
{
EventFlags ev;
uint32_t flags;
ev.set(FLAG01 | FLAG02 | FLAG03);
#if !MBED_TRAP_ERRORS_ENABLED
flags = ev.clear(PROHIBITED_FLAG);
TEST_ASSERT_EQUAL(osFlagsErrorParameter, flags);
#endif
flags = ev.get();
TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, flags);
#if !MBED_TRAP_ERRORS_ENABLED
flags = ev.set(PROHIBITED_FLAG);
TEST_ASSERT_EQUAL(osFlagsErrorParameter, flags);
#endif
flags = ev.get();
TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, flags);
}
/** Test set/get/clear for full flag range
Given a EventFlags object
When call @a clear
Then @a clear return status is already set flags
When call @a set with specified flag
Then @a set return status is flags after setting
When call @a get
Then @a get return status is set flags
*/
void test_set_get_clear_full_flag_range(void)
{
EventFlags ev;
uint32_t flag, flags, ret;
flags = NO_FLAGS;
for (int i = 0; i <= MAX_FLAG_POS; i++) {
ret = ev.clear();
TEST_ASSERT_EQUAL(flags, ret);
flags = 1 << i;
ret = ev.set(flags);
TEST_ASSERT_EQUAL(flags, ret);
ret = ev.get();
TEST_ASSERT_EQUAL(flags, ret);
}
ev.clear();
flags = NO_FLAGS;
for (int i = 0; i <= MAX_FLAG_POS; i++) {
ret = ev.clear(NO_FLAGS);
TEST_ASSERT_EQUAL(flags, ret);
flag = 1 << i;
flags |= flag;
ret = ev.set(flag);
TEST_ASSERT_EQUAL(flags, ret);
ret = ev.get();
TEST_ASSERT_EQUAL(flags, ret);
}
}
#if defined(MBED_CONF_RTOS_PRESENT)
/** Test if multi-threaded flag set cause wait_all to return
Given a EventFlags object and three threads are started in parallel
When threads set specified flags
Then main thread waits until receive all of them
*/
void test_multi_thread_all(void)
{
EventFlags ef;
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { send_thread(&ef, FLAG01, 1ms); });
thread2.start([&] { send_thread(&ef, FLAG02, 2ms); });
thread3.start([&] { send_thread(&ef, FLAG03, 3ms); });
uint32_t ret = ef.wait_all(FLAG01 | FLAG02 | FLAG03);
TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, ret);
}
/** Test if multi-threaded flag set cause wait_any to return
Given a EventFlags object and three threads are started in parallel
When threads set specified flags
Then main thread waits until receive all of them
*/
void test_multi_thread_any(void)
{
EventFlags ef;
uint32_t ret;
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { send_thread(&ef, FLAG01, 1ms); });
thread2.start([&] { send_thread(&ef, FLAG02, 1ms); });
thread3.start([&] { send_thread(&ef, FLAG03, 1ms); });
for (int i = 0; i <= MAX_FLAG_POS; i++) {
uint32_t flag = 1 << i;
ret = ef.wait_any(flag);
TEST_ASSERT(flag | ret);
}
ret = ef.get();
TEST_ASSERT_EQUAL(NO_FLAGS, ret);
}
/** Test if multi-threaded flag set cause wait_any(with timeout) to return
Given a EventFlags object and thread is running
When main thread call @ wait_any with timeout
Then when timeout expires @ wait_any return status is osFlagsErrorTimeout
When main thread call @ wait_any with timeout and thread set specified flags
Then main thread waits until receive all of them and @ wait_any return status is wait flag
*/
void test_multi_thread_any_timeout(void)
{
EventFlags ef;
uint32_t ret;
Thread thread(osPriorityNormal, THREAD_STACK_SIZE);
thread.start([&] { send_thread_sync(&ef, FLAG01 | FLAG02 | FLAG03, 1ms); });
for (int i = 0; i <= MAX_FLAG_POS; i++) {
uint32_t flag = 1 << i;
ret = ef.wait_any_for(flag, 10ms);
TEST_ASSERT_EQUAL(osFlagsErrorTimeout, ret);
sync_sem.release();
ret = ef.wait_any_for(flag, 10ms);
TEST_ASSERT_EQUAL(flag, ret);
}
ret = ef.get();
TEST_ASSERT_EQUAL(NO_FLAGS, ret);
}
/** Test if multi-threaded flag set cause wait_any(without clear) to return
Given a EventFlags object and three threads are started in parallel
When threads set specified flags
Then main thread waits until receive all of them
*/
void test_multi_thread_any_no_clear(void)
{
EventFlags ef;
uint32_t ret;
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { send_thread(&ef, FLAG01, 1ms); });
thread2.start([&] { send_thread(&ef, FLAG02, 1ms); });
thread3.start([&] { send_thread(&ef, FLAG03, 1ms); });
for (int i = 0; i <= MAX_FLAG_POS; i++) {
uint32_t flag = 1 << i;
ret = ef.wait_any_for(flag, Kernel::wait_for_u32_forever, false);
TEST_ASSERT(flag | ret);
ret = ef.clear(flag);
TEST_ASSERT(ret < osFlagsError);
}
ret = ef.get();
TEST_ASSERT_EQUAL(NO_FLAGS, ret);
}
/** Test multi-threaded wait_any
Given a EventFlags object and three threads are started in parallel
When flags are set in main thread
Then other threads waits until receive all of them
*/
void test_multi_thread_all_many_wait(void)
{
EventFlags ef;
{
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { wait_thread_all(&ef, FLAG01); });
thread2.start([&] { wait_thread_all(&ef, FLAG02); });
thread3.start([&] { wait_thread_all(&ef, FLAG03); });
ef.set(FLAG01 | FLAG02 | FLAG03);
thread1.join();
thread2.join();
thread3.join();
TEST_ASSERT_EQUAL(NO_FLAGS, ef.get());
}
{
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { wait_thread_all(&ef, FLAG01); });
thread2.start([&] { wait_thread_all(&ef, FLAG02); });
thread3.start([&] { wait_thread_all(&ef, FLAG03); });
ef.set(FLAG01);
thread1.join();
ef.set(FLAG02);
thread2.join();
ef.set(FLAG03);
thread3.join();
TEST_ASSERT_EQUAL(NO_FLAGS, ef.get());
}
}
#endif
/** Test if multi-event flag set cause wait_all to return
Given a EventFlags object and three ticker instance with the callback registered
When callbacks set specified flags
Then main thread waits until receive all of them
*/
void test_multi_eventflags_all(void)
{
EventFlags ef;
Ticker t1, t2, t3;
t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms);
t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms);
t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms);
uint32_t ret = ef.wait_all_for(FLAG01 | FLAG02 | FLAG03, 20ms, false);
TEST_ASSERT_EQUAL(FLAG01 | FLAG02 | FLAG03, ret);
}
/** Test if multi-event flag set cause wait_any to return
Given a EventFlags object and three ticker instance with the callback registered
When callbacks set specified flags
Then main thread waits until receive all of them
*/
void test_multi_eventflags_any(void)
{
EventFlags ef;
uint32_t ret;
Ticker t1, t2, t3;
t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms);
t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms);
t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms);
for (int i = 0; i <= MAX_FLAG_POS; i++) {
uint32_t flag = 1 << i;
ret = ef.wait_any(flag);
TEST_ASSERT(flag | ret);
}
ret = ef.get();
TEST_ASSERT_EQUAL(NO_FLAGS, ret);
}
/** Test if multi-event flag set cause wait_any(without clear) to return
Given a EventFlags object and three ticker instance with the callback registered
When callbacks set specified flags
Then main thread waits until receive all of them
*/
void test_multi_eventflags_any_no_clear(void)
{
EventFlags ef;
uint32_t ret;
Ticker t1, t2, t3;
t1.attach([&] { send_thread(&ef, FLAG01, 0ms); }, 3ms);
t2.attach([&] { send_thread(&ef, FLAG02, 0ms); }, 4ms);
t3.attach([&] { send_thread(&ef, FLAG03, 0ms); }, 5ms);
for (int i = 0; i <= MAX_FLAG_POS; i++) {
uint32_t flag = 1 << i;
ret = ef.wait_any(flag, osWaitForever, false);
TEST_ASSERT(flag | ret);
ret = ef.clear(flag);
TEST_ASSERT(ret < osFlagsError);
}
ret = ef.get();
TEST_ASSERT_EQUAL(NO_FLAGS, ret);
}
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("Test empty clear", test_empty_clear),
Case("Test empty get", test_empty_get),
Case("Test empty set", test_empty_set),
Case("Test clear/set with prohibited flag", test_prohibited),
Case("Test set/get/clear for full flag range", test_set_get_clear_full_flag_range),
#if defined(MBED_CONF_RTOS_PRESENT)
Case("Test multi-threaded wait_all", test_multi_thread_all),
Case("Test multi-threaded wait_any", test_multi_thread_any),
Case("Test multi-threaded wait_all many wait", test_multi_thread_all_many_wait),
Case("Test multi-threaded wait_any timeout", test_multi_thread_any_timeout),
Case("Test multi-threaded wait_any no clear", test_multi_thread_any_no_clear),
#endif
Case("Test multi-eventflags wait_all", test_multi_eventflags_all),
Case("Test multi-eventflags wait_any", test_multi_eventflags_any),
Case("Test multi-eventflags wait_any no clear", test_multi_eventflags_any_no_clear)
};
utest::v1::Specification specification(test_setup, cases);
int main()
{
return !utest::v1::Harness::run(specification);
}
#endif // !DEVICE_USTICKER

View File

@@ -0,0 +1,293 @@
/*
* Copyright (c) 2016-2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(TARGET_CORTEX_A) || !DEVICE_USTICKER
#error [NOT_SUPPORTED] test not supported.
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mbed.h"
#include "cmsis.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
using utest::v1::Case;
static const int test_timeout = 30;
// Amount to malloc for each iteration
#define MALLOC_TEST_SIZE 256
// Malloc fill pattern
#define MALLOC_FILL 0x55
extern unsigned char *mbed_heap_start;
extern uint32_t mbed_heap_size;
extern unsigned char *mbed_stack_isr_start;
extern uint32_t mbed_stack_isr_size;
#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP)
extern uint32_t __mbed_sbrk_start_0;
extern uint32_t __mbed_krbs_start_0;
unsigned char *mbed_heap_start_0 = (unsigned char *) &__mbed_sbrk_start_0;;
uint32_t mbed_heap_size_0 = (uint32_t) &__mbed_krbs_start_0 - (uint32_t) &__mbed_sbrk_start_0;
#endif
struct linked_list {
linked_list *next;
uint8_t data[MALLOC_TEST_SIZE];
};
// Global test variables
#define TEST_VALUE 789
static struct Test {
Test() : val(TEST_VALUE) {}
~Test() {}
int val;
} t;
int test_function()
{
return TEST_VALUE;
}
static int global_int = test_function();
/** Test global variables initialisation
*/
void test_global_variables_initialisation(void)
{
TEST_ASSERT_EQUAL(TEST_VALUE, global_int);
TEST_ASSERT_EQUAL(TEST_VALUE, t.val);
}
/* TODO: add memory layout test.
*
* The test was skipped for now since not all devices seems to comply with Mbed OS memory.
*
* @note Mbed OS memory model: https://os.mbed.com/docs/latest/reference/memory.html
*
*/
/*
* Return true if addr is in range [start:start+size)
*/
static bool inrange(uint32_t addr, uint32_t start, uint32_t size)
{
return (addr >= start) && (addr < (start + size));
}
/*
* Return true if [addr:addr+size] is inside [start:start+len]
*/
static bool rangeinrange(uint32_t addr, uint32_t size, uint32_t start, uint32_t len)
{
if ((addr + size) > (start + len)) {
return false;
}
if (addr < start) {
return false;
}
return true;
}
/*
* Return true if the region is filled only with the specified value
*/
static bool valid_fill(uint8_t *data, uint32_t size, uint8_t fill)
{
for (uint32_t i = 0; i < size; i++) {
if (data[i] != fill) {
return false;
}
}
return true;
}
static void allocate_and_fill_heap(linked_list *&head)
{
linked_list *current;
current = (linked_list *) malloc(sizeof(linked_list));
TEST_ASSERT_NOT_NULL(current);
current->next = NULL;
memset((void *) current->data, MALLOC_FILL, sizeof(current->data));
// Allocate until malloc returns NULL
head = current;
while (true) {
// Allocate
linked_list *temp = (linked_list *) malloc(sizeof(linked_list));
if (NULL == temp) {
break;
}
bool result = rangeinrange((uint32_t) temp, sizeof(linked_list), (uint32_t)mbed_heap_start, mbed_heap_size);
#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP)
if (false == result) {
result = rangeinrange((uint32_t) temp, sizeof(linked_list), (uint32_t)mbed_heap_start_0, mbed_heap_size_0);
}
#endif
TEST_ASSERT_TRUE_MESSAGE(result, "Memory allocation out of range");
// Init
temp->next = NULL;
memset((void *) temp->data, MALLOC_FILL, sizeof(current->data));
// Add to list
current->next = temp;
current = temp;
}
}
static void check_and_free_heap(linked_list *head, uint32_t &max_allocation_size)
{
uint32_t total_size = 0;
linked_list *current = head;
while (current != NULL) {
total_size += sizeof(linked_list);
bool result = valid_fill(current->data, sizeof(current->data), MALLOC_FILL);
TEST_ASSERT_TRUE_MESSAGE(result, "Memory fill check failed");
linked_list *next = current->next;
free(current);
current = next;
}
max_allocation_size = total_size;
}
/** Test heap allocation
Given a heap
When memory is allocated from heap
Then the memory is within heap boundary
*/
void test_heap_in_range(void)
{
char *initial_heap;
// Sanity check malloc
initial_heap = (char *) malloc(1);
TEST_ASSERT_NOT_NULL(initial_heap);
bool result = inrange((uint32_t) initial_heap, (uint32_t)mbed_heap_start, mbed_heap_size);
#if defined(TOOLCHAIN_GCC_ARM) && defined(MBED_SPLIT_HEAP)
if (false == result) {
result = inrange((uint32_t) initial_heap, (uint32_t)mbed_heap_start_0, mbed_heap_size_0);
}
#endif
TEST_ASSERT_TRUE_MESSAGE(result, "Heap in wrong location");
free(initial_heap);
}
#if MBED_CONF_RTOS_PRESENT
/** Test for Main thread stack
Given a Main thread and its stack
When check Main thread stack pointer
Then the SP is within Main stack boundary
*/
void test_main_stack_in_range(void)
{
mbed_rtos_storage_thread_t *thread = (mbed_rtos_storage_thread_t *) osThreadGetId();
uint32_t psp = __get_PSP();
uint8_t *stack_mem = (uint8_t *) thread->stack_mem;
uint32_t stack_size = thread->stack_size;
// PSP stack should be somewhere in the middle
bool result = inrange(psp, (uint32_t) stack_mem, stack_size);
TEST_ASSERT_TRUE_MESSAGE(result, "Main stack in wrong location");
}
#endif // #if MBED_CONF_RTOS_PRESENT
/** Test for Scheduler/ISR thread stack
Given a Scheduler/ISR thread and its stack
When check Scheduler/ISR thread stack pointer
Then the SP is within Scheduler/ISR stack boundary
*/
void test_isr_stack_in_range(void)
{
// MSP stack should be very near end (test using within 128 bytes)
uint32_t msp = __get_MSP();
bool result = inrange(msp, (uint32_t)mbed_stack_isr_start + mbed_stack_isr_size - 0x400, 0x400);
TEST_ASSERT_TRUE_MESSAGE(result, "Interrupt stack in wrong location");
}
/** Test full heap allocation
Given a heap and linked_list data structure
When linked_list is filled till run out of heap memory
Then the memory is properly initialised and freed
*/
void test_heap_allocation_free(void)
{
linked_list *head = NULL;
uint32_t max_allocation_size = 0;
// Fully allocate the heap and stack
allocate_and_fill_heap(head);
check_and_free_heap(head, max_allocation_size);
// Force a task switch so a stack check is performed
ThisThread::sleep_for(10);
printf("Total size dynamically allocated: %luB\n", max_allocation_size);
}
// Test cases
Case cases[] = {
Case("Test global variables initialisation", test_global_variables_initialisation),
Case("Test heap in range", test_heap_in_range),
#if MBED_CONF_RTOS_PRESENT
Case("Test main stack in range", test_main_stack_in_range),
#endif
Case("Test isr stack in range", test_isr_stack_in_range),
Case("Test heap allocation and free", test_heap_allocation_free)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(test_timeout, "default_auto");
return utest::v1::greentea_test_setup_handler(number_of_cases);
}
utest::v1::Specification specification(greentea_test_setup, cases);
int main()
{
return !utest::v1::Harness::run(specification);
}
#endif // defined(TARGET_CORTEX_A) || !DEVICE_USTICKER

View File

@@ -0,0 +1,135 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
#include "rtos/Kernel.h"
#include "mbed.h"
#include <type_traits>
using namespace std::chrono;
using utest::v1::Case;
#define TEST_REPEAT_COUNT 1000
#define ACCURACY_DURATION 1s
#if defined(NO_SYSTICK) || defined(MBED_TICKLESS)
// On targets with NO_SYSTICK/MBED_TICKLESS enabled, systick is emulated by lp_ticker what makes it less accurate
// for more details https://os.mbed.com/docs/latest/reference/tickless.html
#define ACCURACY_DELTA 15000us // 1.5%
#else
#define ACCURACY_DELTA 1500us // 0.15%
#endif
#define TEST_ASSERT_EQUAL_DURATION(expected, actual) \
do { \
using ct = std::common_type_t<decltype(expected), decltype(actual)>; \
TEST_ASSERT_EQUAL(ct(expected).count(), ct(actual).count()); \
} while (0)
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
/** Test if declared kernel ticker frequency is 1kHz
Given a RTOS kernel ticker
When check it frequency
Then the the frequency is 1kHz
*/
void test_frequency()
{
#if defined(MBED_CONF_RTOS_PRESENT)
uint32_t freq = osKernelGetTickFreq();
TEST_ASSERT_EQUAL_UINT32_MESSAGE(1000, freq, "Expected SysTick frequency is 1kHz");
#endif
TEST_ASSERT_TRUE_MESSAGE((std::ratio_equal<rtos::Kernel::Clock::period, std::milli>::value), "Expected Kernel::Clock frequency is 1kHz");
}
/** Test if kernel ticker increments by one
Given a RTOS kernel ticker
When perform subsequent calls of @a rtos::Kernel::Clock::now
Then subsequent reads should not differ by more than one
*/
void test_increment(void)
{
for (uint32_t i = 0; i < TEST_REPEAT_COUNT; i++) {
auto start = rtos::Kernel::Clock::now();
while (true) {
rtos::Kernel::Clock::duration diff = rtos::Kernel::Clock::now() - start;
if (diff.count() != 0) {
TEST_ASSERT_EQUAL_INT64(1, diff.count());
break;
}
}
}
}
/** Test if kernel ticker rate is correct
Given a RTOS kernel ticker
When perform subsequent calls of @a rtos::Kernel::Clock::now
Then when it reports 1 second elapsed, the time measured using a high-res Timer corresponds.
*/
void test_accuracy()
{
Kernel::Clock::time_point start, stop;
Timer timer;
start = rtos::Kernel::Clock::now();
// wait for tick
do {
stop = rtos::Kernel::Clock::now();
} while (stop == start);
timer.start();
start = stop;
// wait for 1 second to elapse according to kernel
do {
stop = rtos::Kernel::Clock::now();
} while ((stop - start) < ACCURACY_DURATION);
timer.stop();
TEST_ASSERT_DURATION_WITHIN(ACCURACY_DELTA, ACCURACY_DURATION, timer.elapsed_time());
}
// Test cases
Case cases[] = {
Case("Test kernel ticker declared frequency", test_frequency),
Case("Test if kernel ticker increments by one", test_increment),
Case("Test kernel ticker accuracy", test_accuracy)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return utest::v1::greentea_test_setup_handler(number_of_cases);
}
utest::v1::Specification specification(greentea_test_setup, cases);
int main()
{
return !utest::v1::Harness::run(specification);
}

View File

@@ -0,0 +1,510 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] mail test cases require RTOS with multithread to run
#else
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
using namespace utest::v1;
using namespace std::chrono;
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
#define THREAD_STACK_SIZE 512
#elif defined(TARGET_ARM_FM)
#define THREAD_STACK_SIZE 512
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
#define THREAD_STACK_SIZE 512
#else
#define THREAD_STACK_SIZE 320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/
#endif
#define QUEUE_SIZE 16
#define THREAD_1_ID 1
#define THREAD_2_ID 2
#define THREAD_3_ID 3
#define QUEUE_PUT_DELAY_1 5ms
#define QUEUE_PUT_DELAY_2 50ms
#define QUEUE_PUT_DELAY_3 100ms
#define DATA_BASE 100
typedef struct {
uint16_t data;
uint8_t thread_id;
} mail_t;
void send_thread(Mail<mail_t, QUEUE_SIZE> *m, uint8_t thread_id, milliseconds wait, uint32_t send_count)
{
uint32_t data = thread_id * DATA_BASE;
for (uint32_t i = 0; i < send_count; i++) {
mail_t *mail = m->try_alloc();
mail->thread_id = thread_id;
mail->data = data++;
m->put(mail);
ThisThread::sleep_for(wait);
}
}
template<uint32_t queue_size>
void receive_thread(Mail<mail_t, queue_size> *m, uint8_t thread_id, milliseconds wait)
{
int result_counter = 0;
uint32_t data = thread_id * DATA_BASE;
ThisThread::sleep_for(wait);
for (uint32_t i = 0; i < queue_size; i++) {
mail_t *mail = m->try_get_for(Kernel::wait_for_u32_forever);
if (mail) {
const uint8_t id = mail->thread_id;
// verify thread id
TEST_ASSERT_TRUE(id == thread_id);
// verify sent data
TEST_ASSERT_TRUE(mail->data == data++);
m->free(mail);
result_counter++;
}
}
TEST_ASSERT_EQUAL(queue_size, result_counter);
}
/** Test single thread Mail usage and order
Given mailbox and one additional thread
When messages are put in to the Mail box by this thread
Then messages are received in main thread in the same order as was sent and the data sent is valid
*/
void test_single_thread_order(void)
{
uint16_t data = DATA_BASE;
int result_counter = 0;
Mail<mail_t, QUEUE_SIZE> mail_box;
// mail send thread creation
Thread thread(osPriorityNormal, THREAD_STACK_SIZE);
thread.start([&] { send_thread(&mail_box, THREAD_1_ID, QUEUE_PUT_DELAY_1, QUEUE_SIZE); });
// wait for some mail to be collected
ThisThread::sleep_for(10ms);
for (uint32_t i = 0; i < QUEUE_SIZE; i++) {
// mail receive (main thread)
mail_t *mail = mail_box.try_get_for(Kernel::wait_for_u32_forever);
if (mail) {
const uint8_t id = mail->thread_id;
// verify thread id
TEST_ASSERT_TRUE(id == THREAD_1_ID);
// verify sent data
TEST_ASSERT_TRUE(mail->data == data++);
mail_box.free(mail);
result_counter++;
}
}
TEST_ASSERT_EQUAL(QUEUE_SIZE, result_counter);
}
/** Test multi thread Mail usage and order
Given mailbox and three additional threads
When messages are put in to the Mail box by these threads
Then messages are received in main thread in the same per thread order as was sent and the data sent is valid
*/
void test_multi_thread_order(void)
{
uint16_t data[4] = { 0, DATA_BASE, DATA_BASE * 2, DATA_BASE * 3 };
int result_counter = 0;
Mail<mail_t, QUEUE_SIZE> mail_box;
// mail send threads creation
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { send_thread(&mail_box, THREAD_1_ID, QUEUE_PUT_DELAY_1, 7); });
thread2.start([&] { send_thread(&mail_box, THREAD_2_ID, QUEUE_PUT_DELAY_2, 5); });
thread3.start([&] { send_thread(&mail_box, THREAD_3_ID, QUEUE_PUT_DELAY_3, 4); });
// wait for some mail to be collected
ThisThread::sleep_for(10ms);
for (uint32_t i = 0; i < QUEUE_SIZE; i++) {
// mail receive (main thread)
mail_t *mail = mail_box.try_get_for(Kernel::wait_for_u32_forever);
if (mail) {
const uint8_t id = mail->thread_id;
// verify thread id
TEST_ASSERT_TRUE((id == THREAD_1_ID) || (id == THREAD_2_ID) || (id == THREAD_3_ID));
// verify sent data
TEST_ASSERT_TRUE(mail->data == data[id]++);
mail_box.free(mail);
result_counter++;
}
}
TEST_ASSERT_EQUAL(QUEUE_SIZE, result_counter);
}
/** Test multi thread multi Mail usage and order
Given 3 mailbox and three additional threads
When messages are put in to the mail boxes by main thread
Then messages are received by threads in the same per mail box order as was sent and the data sent is valid
*/
void test_multi_thread_multi_mail_order(void)
{
Mail<mail_t, 4> mail_box[4]; /* mail_box[0] not used */
uint16_t data[4] = { 0, DATA_BASE, DATA_BASE * 2, DATA_BASE * 3 };
mail_t *mail;
uint8_t id;
// mail receive threads creation
Thread thread1(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread2(osPriorityNormal, THREAD_STACK_SIZE);
Thread thread3(osPriorityNormal, THREAD_STACK_SIZE);
thread1.start([&] { receive_thread<4>(mail_box + 1, THREAD_1_ID, 0ms); });
thread2.start([&] { receive_thread<4>(mail_box + 2, THREAD_2_ID, 10ms); });
thread3.start([&] { receive_thread<4>(mail_box + 3, THREAD_3_ID, 100ms); });
for (uint32_t i = 0; i < 4; i++) {
id = THREAD_1_ID;
mail = mail_box[id].try_alloc();
mail->thread_id = id;
mail->data = data[id]++;
mail_box[id].put(mail);
id = THREAD_2_ID;
mail = mail_box[id].try_alloc();
mail->thread_id = id;
mail->data = data[id]++;
mail_box[id].put(mail);
id = THREAD_3_ID;
mail = mail_box[id].try_alloc();
mail->thread_id = id;
mail->data = data[id]++;
mail_box[id].put(mail);
ThisThread::sleep_for(i * 10ms);
}
thread1.join();
thread2.join();
thread3.join();
}
/** Test message memory deallocation with block out of the scope
Given an empty mailbox
When try to free out of the scope memory block
Then it return appropriate error code
*/
void test_free_wrong()
{
osStatus status;
Mail<uint32_t, 4> mail_box;
uint32_t *mail, data;
mail = &data;
status = mail_box.free(mail);
TEST_ASSERT_EQUAL(osErrorParameter, status);
mail = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail);
mail = &data;
status = mail_box.free(mail);
TEST_ASSERT_EQUAL(osErrorParameter, status);
}
/** Test message memory deallocation with null block
Given an empty mailbox
When try to free null ptr
Then it return appropriate error code
*/
void test_free_null()
{
osStatus status;
Mail<uint32_t, 4> mail_box;
uint32_t *mail;
mail = NULL;
status = mail_box.free(mail);
TEST_ASSERT_EQUAL(osErrorParameter, status);
mail = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail);
mail = NULL;
status = mail_box.free(mail);
TEST_ASSERT_EQUAL(osErrorParameter, status);
}
/** Test get from empty mailbox with timeout set
Given an empty mailbox
When @a try_get_for is called on the mailbox with timeout of 50ms
Then mailbox returns no data
*/
void test_get_empty_timeout()
{
Mail<uint32_t, 4> mail_box;
Timer timer;
timer.start();
uint32_t *mail = mail_box.try_get_for(50ms);
TEST_ASSERT_DURATION_WITHIN(5ms, 50ms, timer.elapsed_time());
TEST_ASSERT_NULL(mail);
}
/** Test get from empty mailbox with 0 timeout
Given an empty mailbox
When @a try_get is called on the mailbox
Then mailbox returns no data
*/
void test_get_empty_no_timeout()
{
Mail<uint32_t, 4> mail_box;
uint32_t *mail = mail_box.try_get();
TEST_ASSERT_NULL(mail);
}
/** Test mail order
Given an mailbox for uint32_t values
Then allocate two mails and put them in to mailbox
When call @a get it returns previously put mails
Then mails should be in the same order as put
*/
void test_order(void)
{
osStatus status;
Mail<int32_t, 4> mail_box;
const int32_t TEST_VAL1 = 123;
const int32_t TEST_VAL2 = 456;
int32_t *mail1 = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail1);
*mail1 = TEST_VAL1;
mail_box.put(mail1);
int32_t *mail2 = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail2);
*mail2 = TEST_VAL2;
mail_box.put(mail2);
mail1 = mail_box.try_get_for(Kernel::wait_for_u32_forever);
TEST_ASSERT_NOT_NULL(mail1);
TEST_ASSERT_EQUAL(TEST_VAL1, *mail1);
mail2 = mail_box.try_get_for(Kernel::wait_for_u32_forever);
TEST_ASSERT_NOT_NULL(mail2);
TEST_ASSERT_EQUAL(TEST_VAL2, *mail2);
status = mail_box.free(mail1);
TEST_ASSERT_EQUAL(osOK, status);
status = mail_box.free(mail2);
TEST_ASSERT_EQUAL(osOK, status);
}
/** Test Mail box max size limit
Given an Mail box with max size of 4 elements
When call @a try_alloc four times it returns memory blocks
Then the memory blocks should be valid
When call @a try_alloc one more time it returns memory blocks
Then the memory blocks should be not valid (NULL - no memory available)
*/
void test_max_size()
{
osStatus status;
Mail<uint32_t, 4> mail_box;
// 1 OK
uint32_t *mail1 = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail1);
// 2 OK
uint32_t *mail2 = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail2);
// 3 OK
uint32_t *mail3 = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail3);
// 4 OK
uint32_t *mail4 = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail4);
// 5 KO
uint32_t *mail5 = mail_box.try_alloc();
TEST_ASSERT_EQUAL(NULL, mail5);
status = mail_box.free(mail1);
TEST_ASSERT_EQUAL(osOK, status);
status = mail_box.free(mail2);
TEST_ASSERT_EQUAL(osOK, status);
status = mail_box.free(mail3);
TEST_ASSERT_EQUAL(osOK, status);
status = mail_box.free(mail4);
TEST_ASSERT_EQUAL(osOK, status);
}
/** Test mailbox of T type data
Given an mailbox with T memory block type
When allocate/put/get/free memory block
Then all operations should succeed
*/
template<typename T>
void test_data_type(void)
{
osStatus status;
Mail<T, 4> mail_box;
const T TEST_VAL = 123;
T *mail = mail_box.try_alloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail);
*mail = TEST_VAL;
mail_box.put(mail);
mail = mail_box.try_get_for(Kernel::wait_for_u32_forever);
TEST_ASSERT_NOT_NULL(mail);
TEST_ASSERT_EQUAL(TEST_VAL, *mail);
status = mail_box.free(mail);
TEST_ASSERT_EQUAL(osOK, status);
}
/** Test try_calloc - memory block allocation with resetting
Given an empty Mail box
When call @a try_calloc it returns allocated memory block
Then the memory block should be valid and filled with zeros
*/
void test_calloc()
{
Mail<uint32_t, 1> mail_box;
uint32_t *mail = mail_box.try_calloc();
TEST_ASSERT_NOT_EQUAL(NULL, mail);
TEST_ASSERT_EQUAL(0, *mail);
}
/** Test mail empty
Given a mail of uint32_t data
before data is inserted the mail should be empty
after data is inserted the mail shouldn't be empty
*/
void test_mail_empty()
{
Mail<mail_t, 1> m;
mail_t *mail = m.try_alloc();
TEST_ASSERT_EQUAL(true, m.empty());
m.put(mail);
TEST_ASSERT_EQUAL(false, m.empty());
}
/** Test mail empty
Given a mail of uint32_t data with size of 1
before data is inserted the mail shouldn't be full
after data is inserted the mail should be full
*/
void test_mail_full()
{
Mail<mail_t, 1> m;
mail_t *mail = m.try_alloc();
TEST_ASSERT_EQUAL(false, m.full());
m.put(mail);
TEST_ASSERT_EQUAL(true, m.full());
}
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Test try_calloc", test_calloc),
Case("Test message type uint8", test_data_type<uint8_t>),
Case("Test message type uint16", test_data_type<uint16_t>),
Case("Test message type uint32", test_data_type<uint32_t>),
Case("Test mailbox max size", test_max_size),
Case("Test message send order", test_order),
Case("Test get with timeout on empty mailbox", test_get_empty_timeout),
Case("Test get without timeout on empty mailbox", test_get_empty_no_timeout),
Case("Test null message free", test_free_null),
Case("Test invalid message free", test_free_wrong),
Case("Test message send/receive single thread and order", test_single_thread_order),
Case("Test message send/receive multi-thread and per thread order", test_multi_thread_order),
Case("Test message send/receive multi-thread, multi-Mail and per thread order", test_multi_thread_multi_mail_order),
Case("Test mail empty", test_mail_empty),
Case("Test mail full", test_mail_full)
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}
#endif // !DEVICE_USTICKER
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)

View File

@@ -0,0 +1,225 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] test not supported
#else
using utest::v1::Case;
extern uint32_t mbed_heap_size;
static const int test_timeout = 25;
volatile bool thread_should_continue = true;
#define NUM_THREADS 4
#define THREAD_MALLOC_SIZE 100
#if defined(__CORTEX_A9)
#define THREAD_STACK_SIZE 512
#elif defined(__CORTEX_M23) || defined(__CORTEX_M33)
#define THREAD_STACK_SIZE 512
#elif defined(TARGET_ARM_FM)
#define THREAD_STACK_SIZE 512
#elif defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
#define THREAD_STACK_SIZE 512
#else
#define THREAD_STACK_SIZE 256
#endif
void task_using_malloc(void)
{
void *data = NULL;
while (thread_should_continue) {
// Repeatedly allocate and free memory
data = malloc(THREAD_MALLOC_SIZE);
TEST_ASSERT_NOT_NULL(data);
// test whole allocated memory
memset(data, 0, THREAD_MALLOC_SIZE);
free(data);
}
}
/** Test for multithreaded heap allocations
Given multiple threads are started in parallel
When each of the threads allocate memory
Then the memory allocation succeed and @a malloc return valid memory
*/
#if defined(MBED_CONF_RTOS_PRESENT)
void test_multithread_allocation(void)
{
// static stack for threads to reduce heap usage on devices with small RAM
// and eliminate run out of heap memory problem
uint8_t stack[NUM_THREADS][THREAD_STACK_SIZE];
bool thread_alloc_failure = false;
Thread *thread_list[NUM_THREADS];
int test_time = 20;
// Allocate threads for the test
for (int i = 0; i < NUM_THREADS; i++) {
thread_list[i] = new Thread(osPriorityNormal, THREAD_STACK_SIZE, stack[i]);
if (NULL == thread_list[i]) {
thread_alloc_failure = true;
} else {
thread_list[i]->start(task_using_malloc);
}
}
// Give the test time to run
while (test_time--) {
ThisThread::sleep_for(1000);
}
// Join and delete all threads
thread_should_continue = false;
for (int i = 0; i < NUM_THREADS; i++) {
if (NULL != thread_list[i]) {
thread_list[i]->join();
delete thread_list[i];
thread_list[i] = NULL;
}
}
TEST_ASSERT_FALSE(thread_alloc_failure);
}
#endif
/** Test for multiple heap alloc and free calls */
#define ALLOC_ARRAY_SIZE 100
#define ALLOC_LOOP 20
#define SIZE_INCREMENTS 1023
#define SIZE_MODULO 31
void test_alloc_and_free(void)
{
void *array[ALLOC_ARRAY_SIZE];
void *data = NULL;
long total_allocated = 0;
int count = 0;
int size = SIZE_INCREMENTS;
int loop = ALLOC_LOOP;
while (loop) {
data = count < ALLOC_ARRAY_SIZE ? malloc(size) : NULL;
if (NULL != data) {
array[count++] = data;
memset((void *)data, 0xdeadbeef, size);
total_allocated += size;
size += SIZE_INCREMENTS;
if (size > 10000) {
size %= SIZE_MODULO;
}
} else {
for (int i = 0; i < count; i++) {
free(array[i]);
array[i] = NULL;
}
loop--;
printf("Total size dynamically allocated: %luB\n", total_allocated);
total_allocated = 0;
count = 0;
continue;
}
}
}
/** Test for large heap allocation
Given a heap of size mbed_heap_size
When try to allocate memory of size mbed_heap_size/5 (20% of whole heap)
Then the memory is allocated and @a malloc return valid memory
*/
void test_big_allocation(void)
{
const uint32_t alloc_size = mbed_heap_size / 5;
void *data = NULL;
data = malloc(alloc_size);
TEST_ASSERT_NOT_NULL(data);
// test whole allocated memory
memset(data, 0, alloc_size);
free(data);
}
/** Test if allocation of zero size does not cause any undefined behaviour
Given a heap
When try to allocate memory of size 0
Then the return value of @a malloc depends on the particular library implementation
(NULL or smallest possible allocation) and no undefined behaviour happens
@note If allocation size is zero, the return value depends on the particular library implementation
(it may or may not be a null pointer), but the returned pointer shall not be dereferenced
*/
void test_zero_allocation(void)
{
void *data = NULL;
data = malloc(0);
if (data != NULL) {
free(data);
}
TEST_ASSERT_MESSAGE(true, "malloc(0) succeed - no undefined behaviour happens");
}
/** Test if free on NULL pointer does not cause any undefined behaviour
Given a NULL pointer
When try to free it
Then the function @a free does nothing and no undefined behaviour happens
*/
void test_null_free(void)
{
void *data = NULL;
free(data);
TEST_ASSERT_MESSAGE(true, "free(NULL) succeed - no undefined behaviour happens");
}
// Test cases
Case cases[] = {
Case("Test 0 size allocation", test_zero_allocation),
Case("Test NULL pointer free", test_null_free),
#if defined(MBED_CONF_RTOS_PRESENT)
Case("Test multithreaded allocations", test_multithread_allocation),
#endif
Case("Test large allocation", test_big_allocation),
Case("Test multiple alloc and free calls", test_alloc_and_free)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(test_timeout, "default_auto");
return utest::v1::greentea_test_setup_handler(number_of_cases);
}
utest::v1::Specification specification(greentea_test_setup, cases);
int main()
{
return !utest::v1::Harness::run(specification);
}
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !DEVICE_USTICKER

View File

@@ -0,0 +1,323 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] Mutex test cases require RTOS with multithread to run
#else
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
#include <type_traits>
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
using namespace utest::v1;
using namespace std::chrono;
#if defined(__CORTEX_M23) || defined(__CORTEX_M33)
#define TEST_STACK_SIZE 768
#else
#define TEST_STACK_SIZE 512
#endif
#define TEST_LONG_DELAY 20ms
#define TEST_DELAY 10ms
#define SIGNALS_TO_EMIT 100
Mutex stdio_mutex;
volatile int change_counter = 0;
volatile bool changing_counter = false;
volatile bool mutex_defect = false;
bool manipulate_protected_zone(const Kernel::Clock::duration thread_delay)
{
bool result = true;
osStatus stat;
stdio_mutex.lock();
core_util_critical_section_enter();
if (changing_counter == true) {
result = false;
mutex_defect = true;
}
changing_counter = true;
change_counter++;
core_util_critical_section_exit();
ThisThread::sleep_for(thread_delay);
core_util_critical_section_enter();
changing_counter = false;
core_util_critical_section_exit();
stdio_mutex.unlock();
return result;
}
void test_thread(Kernel::Clock::duration const *thread_delay)
{
while (true) {
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)
{
const Kernel::Clock::duration t1_delay = TEST_DELAY * 1;
const Kernel::Clock::duration t2_delay = TEST_DELAY * 2;
const Kernel::Clock::duration t3_delay = TEST_DELAY * 3;
Thread t2(osPriorityNormal, TEST_STACK_SIZE);
Thread t3(osPriorityNormal, TEST_STACK_SIZE);
t2.start(callback(test_thread, &t2_delay));
t3.start(callback(test_thread, &t3_delay));
while (true) {
// Thread 1 action
ThisThread::sleep_for(t1_delay);
manipulate_protected_zone(t1_delay);
core_util_critical_section_enter();
if (change_counter >= SIGNALS_TO_EMIT or mutex_defect == true) {
core_util_critical_section_exit();
t2.terminate();
t3.terminate();
break;
}
core_util_critical_section_exit();
}
TEST_ASSERT_EQUAL(false, mutex_defect);
}
void test_dual_thread_nolock_lock_thread(Mutex *mutex)
{
osStatus stat;
mutex->lock();
mutex->unlock();
}
void test_dual_thread_nolock_trylock_thread(Mutex *mutex)
{
bool stat_b = mutex->trylock();
TEST_ASSERT_EQUAL(true, stat_b);
mutex->unlock();
}
/** 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 @a lock and @a unlock operations are successfully performed.
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 @a trylock and @a unlock operations are successfully performed.
*/
template <void (*F)(Mutex *)>
void test_dual_thread_nolock(void)
{
Mutex mutex;
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
thread.start(callback(F, &mutex));
ThisThread::sleep_for(TEST_DELAY);
}
void test_dual_thread_lock_unlock_thread(Mutex *mutex)
{
mutex->lock();
}
/** 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)
{
Mutex mutex;
osStatus stat;
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
mutex.lock();
thread.start(callback(test_dual_thread_lock_unlock_thread, &mutex));
mutex.unlock();
ThisThread::sleep_for(TEST_DELAY);
}
void test_dual_thread_lock_trylock_thread(Mutex *mutex)
{
bool stat = mutex->trylock();
TEST_ASSERT_EQUAL(false, stat);
}
void test_dual_thread_lock_lock_thread(Mutex *mutex)
{
Timer timer;
timer.start();
bool stat = mutex->trylock_for(TEST_DELAY);
TEST_ASSERT_EQUAL(false, stat);
TEST_ASSERT_DURATION_WITHIN(5ms, TEST_DELAY, timer.elapsed_time());
}
/** 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 *)>
void test_dual_thread_lock(void)
{
Mutex mutex;
osStatus stat;
Thread thread(osPriorityNormal, TEST_STACK_SIZE);
mutex.lock();
thread.start(callback(F, &mutex));
ThisThread::sleep_for(TEST_LONG_DELAY);
mutex.unlock();
}
/** 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 @a lock and @a unlock operations are successfully performed.
*/
void test_single_thread_lock_recursive(void)
{
Mutex mutex;
osStatus stat;
mutex.lock();
mutex.lock();
mutex.unlock();
mutex.unlock();
}
/** Test single thread trylock
Given a mutex and a single running thread
When thread calls @a trylock and @a unlock on the mutex
Then @a trylock and @a unlock operations are successfully performed.
*/
void test_single_thread_trylock(void)
{
Mutex mutex;
bool stat_b = mutex.trylock();
TEST_ASSERT_EQUAL(true, stat_b);
mutex.unlock();
}
/** Test single thread lock
Given a mutex and a single running thread
When thread calls @a lock and @a unlock on the mutex
Then @a lock and @a unlock operations are successfully performed.
*/
void test_single_thread_lock(void)
{
Mutex mutex;
osStatus stat;
mutex.lock();
mutex.unlock();
}
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Test single thread lock", test_single_thread_lock),
Case("Test single thread trylock", test_single_thread_trylock),
Case("Test single thread lock recursive", test_single_thread_lock_recursive),
Case("Test dual thread lock locked", test_dual_thread_lock<test_dual_thread_lock_lock_thread>),
Case("Test dual thread trylock locked", test_dual_thread_lock<test_dual_thread_lock_trylock_thread>),
Case("Test dual thread lock unlock", test_dual_thread_lock_unlock),
Case("Test dual thread second thread lock", test_dual_thread_nolock<test_dual_thread_nolock_lock_thread>),
Case("Test dual thread second thread trylock", test_dual_thread_nolock<test_dual_thread_nolock_trylock_thread>),
Case("Test multiple thread", test_multiple_threads),
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}
#endif // !DEVICE_USTICKER
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)

View File

@@ -0,0 +1,347 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] Queue test cases require RTOS with multithread to run
#else
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
using namespace utest::v1;
using namespace std::chrono;
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
#define THREAD_STACK_SIZE 512
#define TEST_TIMEOUT 50ms
static uint32_t msg;
static uint32_t msg2;
void thread_put_uint_msg(Queue<uint32_t, 1> *q)
{
ThisThread::sleep_for(TEST_TIMEOUT);
bool stat = q->try_put(&msg);
TEST_ASSERT_TRUE(stat);
}
void thread_get_uint_msg(Queue<uint32_t, 1> *q)
{
ThisThread::sleep_for(TEST_TIMEOUT);
uint32_t *v;
bool stat = q->try_get_for(Kernel::wait_for_u32_forever, &v);
TEST_ASSERT_TRUE(stat)
TEST_ASSERT_EQUAL(&msg, v);
}
/** Test pass msg
Given a queue for uint32_t messages with one slot
When a uin32_t value is inserted into the queue
and a message is extracted from the queue
Then the extracted message is the same as previously inserted message
*/
void test_pass()
{
Queue<uint32_t, 1> q;
bool stat = q.try_put(&msg);
TEST_ASSERT_TRUE(stat)
uint32_t *v;
stat = q.try_get_for(Kernel::wait_for_u32_forever, &v);
TEST_ASSERT_TRUE(stat)
TEST_ASSERT_EQUAL(&msg, v);
}
/** Test pass msg twice
Given a queue for uint32_t messages with one slot
When a uin32_t value is inserted into the queue
and a message is extracted from the queue
and the procedure is repeated with different message
Then the extracted message is the same as previously inserted message for both iterations
*/
void test_pass_twice()
{
Queue<uint32_t, 1> q;
bool stat = q.try_put(&msg);
TEST_ASSERT_TRUE(stat);
uint32_t *v;
stat = q.try_get_for(Kernel::wait_for_u32_forever, &v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg, v);
stat = q.try_put(&msg2);
TEST_ASSERT_TRUE(stat);
stat = q.try_get_for(Kernel::wait_for_u32_forever, &v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg2, v);
}
/** Test get from empty queue
Given an empty queue for uint32_t values
When @a get is called on the queue with timeout of 0
Then queue returns status of false
*/
void test_get_empty_no_timeout()
{
Queue<uint32_t, 1> q;
uint32_t *v;
bool stat = q.try_get(&v);
TEST_ASSERT_FALSE(stat);
}
/** Test get from empty queue with timeout
Given an empty queue for uint32_t values
When @a get is called on the queue with timeout of 50ms
Then queue returns status of osEventTimeout after about 50ms wait
*/
void test_get_empty_timeout()
{
Queue<uint32_t, 1> q;
Timer timer;
timer.start();
uint32_t *v;
bool stat = q.try_get_for(50ms, &v);
TEST_ASSERT_FALSE(stat);
TEST_ASSERT_DURATION_WITHIN(5ms, 50ms, timer.elapsed_time());
}
/** Test get empty wait forever
Given a two threads A & B and a queue for uint32_t values
When thread A calls @a get on an empty queue with osWaitForever
Then the thread A waits for a message to appear in the queue
When thread B puts a message in the queue
Then thread A wakes up and receives it
*/
void test_get_empty_wait_forever()
{
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Queue<uint32_t, 1> q;
t.start(callback(thread_put_uint_msg, &q));
Timer timer;
timer.start();
uint32_t *v;
bool stat = q.try_get_for(Kernel::wait_for_u32_forever, &v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg, v);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time());
}
/** Test put full no timeout
*
* Given a queue with one slot for uint32_t data
* When a thread tries to insert two messages
* Then first operation succeeds and second fails
*/
void test_put_full_no_timeout()
{
Queue<uint32_t, 1> q;
bool stat = q.try_put(&msg);
TEST_ASSERT_TRUE(stat);
stat = q.try_put(&msg);
TEST_ASSERT_FALSE(stat);
}
/** Test put full timeout
*
* Given a queue with one slot for uint32_t data
* When a thread tries to insert two messages with @ TEST_TIMEOUT timeout
* Then first operation succeeds and second fails
*/
void test_put_full_timeout()
{
Queue<uint32_t, 1> q;
bool stat = q.try_put_for(TEST_TIMEOUT, &msg);
TEST_ASSERT_TRUE(stat);
Timer timer;
timer.start();
stat = q.try_put_for(TEST_TIMEOUT, &msg);
TEST_ASSERT_FALSE(stat);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time());
}
/** Test put full wait forever
*
* Given two threads A & B and a queue with one slot for uint32_t data
* When thread A puts a message to the queue and tries to put second one with @a Kernel::wait_for_u32_forever timeout
* Then thread waits for a slot to become empty in the queue
* When thread B takes one message out of the queue
* Then thread A successfully inserts message into the queue
*/
void test_put_full_waitforever()
{
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Queue<uint32_t, 1> q;
t.start(callback(thread_get_uint_msg, &q));
bool stat = q.try_put(&msg);
TEST_ASSERT_TRUE(stat);
Timer timer;
timer.start();
stat = q.try_put_for(Kernel::wait_for_u32_forever, &msg);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_DURATION_WITHIN(TEST_TIMEOUT / 10, TEST_TIMEOUT, timer.elapsed_time());
t.join();
}
/** Test message ordering
Given a queue of uint32_t data
When two messages are inserted with equal priority
Then messages should be returned in the exact order they were inserted
*/
void test_msg_order()
{
Queue<uint32_t, 2> q;
bool stat = q.try_put_for(TEST_TIMEOUT, &msg);
TEST_ASSERT_TRUE(stat);
stat = q.try_put_for(TEST_TIMEOUT, &msg2);
TEST_ASSERT_TRUE(stat);
uint32_t *v;
stat = q.try_get(&v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg, v);
stat = q.try_get(&v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg2, v);
}
/** Test message priority
Given a queue of uint32_t data
When two messages are inserted with ascending priority
Then messages should be returned in descending priority order
*/
void test_msg_prio()
{
Queue<uint32_t, 2> q;
bool stat = q.try_put_for(TEST_TIMEOUT, &msg, 0);
TEST_ASSERT_TRUE(stat);
stat = q.try_put_for(TEST_TIMEOUT, &msg2, 1);
TEST_ASSERT_TRUE(stat);
uint32_t *v;
stat = q.try_get(&v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg2, v);
stat = q.try_get(&v);
TEST_ASSERT_TRUE(stat);
TEST_ASSERT_EQUAL(&msg, v);
}
/** Test queue empty
Given a queue of uint32_t data
before data is inserted the queue should be empty
after data is inserted the queue shouldn't be empty
*/
void test_queue_empty()
{
Queue<uint32_t, 1> q;
TEST_ASSERT_TRUE(q.empty());
q.try_put_for(TEST_TIMEOUT, &msg, 1);
TEST_ASSERT_FALSE(q.empty());
}
/** Test queue empty
Given a queue of uint32_t data with size of 1
before data is inserted the queue shouldn't be full
after data is inserted the queue should be full
*/
void test_queue_full()
{
Queue<uint32_t, 1> q;
TEST_ASSERT_FALSE(q.full());
q.try_put_for(TEST_TIMEOUT, &msg, 1);
TEST_ASSERT_TRUE(q.full());
}
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(5, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Test pass msg", test_pass),
Case("Test pass msg twice", test_pass_twice),
Case("Test get from empty queue no timeout", test_get_empty_no_timeout),
Case("Test get from empty queue timeout", test_get_empty_timeout),
Case("Test get empty wait forever", test_get_empty_wait_forever),
Case("Test put full no timeout", test_put_full_no_timeout),
Case("Test put full timeout", test_put_full_timeout),
Case("Test put full wait forever", test_put_full_waitforever),
Case("Test message ordering", test_msg_order),
Case("Test message priority", test_msg_prio),
Case("Test queue empty", test_queue_empty),
Case("Test queue full", test_queue_full)
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}
#endif // !DEVICE_USTICKER
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)

View File

@@ -0,0 +1,359 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
using namespace utest::v1;
using namespace std::chrono;
struct test_data {
Semaphore *sem;
uint32_t data;
};
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
#if defined(MBED_CONF_RTOS_PRESENT)
#define THREAD_DELAY 30ms
#define SEMAPHORE_SLOTS 2
#define SEM_CHANGES 100
#define SHORT_WAIT 5ms
#define THREAD_STACK_SIZE 320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/
Semaphore two_slots(SEMAPHORE_SLOTS);
volatile int change_counter = 0;
volatile int sem_counter = 0;
volatile bool sem_defect = false;
void test_thread(rtos::Kernel::Clock::duration const *delay)
{
const auto thread_delay = *delay;
while (true) {
two_slots.acquire();
sem_counter++;
const bool sem_lock_failed = sem_counter > SEMAPHORE_SLOTS;
if (sem_lock_failed) {
sem_defect = true;
}
ThisThread::sleep_for(thread_delay);
sem_counter--;
change_counter++;
two_slots.release();
}
}
/* Test multiple threads
Given 3 threads started with different delays and a semaphore with 2 tokens
when each thread runs it tries to acquire a token
then no more than two threads should be able to access protected region
*/
void test_multi()
{
const rtos::Kernel::Clock::duration t1_delay = THREAD_DELAY * 1;
const rtos::Kernel::Clock::duration t2_delay = THREAD_DELAY * 2;
const rtos::Kernel::Clock::duration t3_delay = THREAD_DELAY * 3;
Thread t1(osPriorityNormal, THREAD_STACK_SIZE);
Thread t2(osPriorityNormal, THREAD_STACK_SIZE);
Thread t3(osPriorityNormal, THREAD_STACK_SIZE);
t1.start(callback(test_thread, &t1_delay));
t2.start(callback(test_thread, &t2_delay));
t3.start(callback(test_thread, &t3_delay));
while (true) {
if (change_counter >= SEM_CHANGES or sem_defect == true) {
t1.terminate();
t2.terminate();
t3.terminate();
break;
}
}
}
void single_thread(struct test_data *data)
{
data->sem->acquire();
data->data++;
}
/** Test single thread
Given a two threads A & B and a semaphore (with count of 0) and a counter (equals to 0)
when thread B calls @a acquire
then thread B waits for a token to become available
then the counter is equal to 0
when thread A calls @a release on the semaphore
then thread B acquires a token and increments the counter
then the counter equals to 1
*/
void test_single_thread()
{
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Semaphore sem(0);
struct test_data data;
osStatus res;
data.sem = &sem;
data.data = 0;
res = t.start(callback(single_thread, &data));
TEST_ASSERT_EQUAL(osOK, res);
ThisThread::sleep_for(SHORT_WAIT);
TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state());
TEST_ASSERT_EQUAL(0, data.data);
res = sem.release();
TEST_ASSERT_EQUAL(osOK, res);
ThisThread::sleep_for(SHORT_WAIT);
TEST_ASSERT_EQUAL(1, data.data);
t.join();
}
void timeout_thread(Semaphore *sem)
{
bool acquired = sem->try_acquire_for(30ms);
TEST_ASSERT_FALSE(acquired);
}
/** Test timeout
Given thread and a semaphore with no tokens available
when a thread calls @a try_acquire_for with a timeout of 30ms
then the thread is blocked for up to 30ms and timeouts after.
*/
void test_timeout()
{
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Semaphore sem(0);
osStatus res;
Timer timer;
timer.start();
res = t.start(callback(timeout_thread, &sem));
TEST_ASSERT_EQUAL(osOK, res);
ThisThread::sleep_for(SHORT_WAIT);
TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state());
t.join();
TEST_ASSERT_DURATION_WITHIN(5ms, 30ms, timer.elapsed_time());
}
#endif
void test_ticker_release(struct test_data *data)
{
osStatus res;
data->data++;
res = data->sem->release();
TEST_ASSERT_EQUAL(osOK, res);
}
/** Test semaphore acquire
Given a semaphore with no tokens available and ticker with the callback registered
when the main thread calls @a acquire
then the main thread is blocked
when callback calls @a release on the semaphore
then the main thread is unblocked
*/
void test_semaphore_acquire()
{
Semaphore sem(0);
struct test_data data;
data.sem = &sem;
data.data = 0;
Ticker t1;
t1.attach(callback(test_ticker_release, &data), 3ms);
sem.acquire();
t1.detach();
TEST_ASSERT_EQUAL(1, data.data);
}
void test_ticker_try_acquire(Semaphore *sem)
{
osStatus res;
res = sem->try_acquire();
TEST_ASSERT_FALSE(res);
}
/** Test semaphore try acquire
Given a semaphore with no tokens available and ticker with the callback registered
when callback tries to acquire the semaphore, it fails.
*/
void test_semaphore_try_acquire()
{
Semaphore sem(0);
Ticker t1;
t1.attach(callback(test_ticker_try_acquire, &sem), 3ms);
ThisThread::sleep_for(4ms);
t1.detach();
}
/** Test semaphore try timeout
Given a semaphore with no tokens available
when the main thread calls @a try_acquire_for with 3ms timeout
then the main thread is blocked for 3ms and timeouts after
*/
void test_semaphore_try_timeout()
{
Semaphore sem(0);
bool res;
res = sem.try_acquire_for(3ms);
TEST_ASSERT_FALSE(res);
}
void test_ticker_semaphore_release(struct Semaphore *sem)
{
osStatus res;
res = sem->release();
TEST_ASSERT_EQUAL(osOK, res);
}
/** Test semaphore try acquire timeout
Given a semaphore with no tokens available and ticker with the callback registered
when the main thread calls @a try_acquire_for with 10ms timeout
then the main thread is blocked for up to 10ms
when callback call @a release on the semaphore after 3ms
then the main thread is unblocked.
*/
void test_semaphore_try_acquire_timeout()
{
Semaphore sem(0);
bool res;
Ticker t1;
t1.attach(callback(test_ticker_semaphore_release, &sem), 3ms);
res = sem.try_acquire_for(10ms);
t1.detach();
TEST_ASSERT_TRUE(res);
}
/** Test no timeouts
Test 1 token no timeout
Given thread and a semaphore with one token available
when thread calls @a try_acquire with timeout of 0ms
then the thread acquires the token immediately
Test 0 tokens no timeout
Given thread and a semaphore with no tokens available
when thread calls @a try_acquire with timeout of 0ms
then the thread returns immediately without acquiring a token
*/
template<int T>
void test_no_timeout()
{
Semaphore sem(T);
Timer timer;
timer.start();
bool acquired = sem.try_acquire();
TEST_ASSERT_EQUAL(T > 0, acquired);
TEST_ASSERT_DURATION_WITHIN(5ms, 0ms, timer.elapsed_time());
}
/** Test multiple tokens wait
Given a thread and a semaphore initialized with 5 tokens
when thread calls @a try_acquire 6 times in a loop
then the token counts goes to zero
*/
void test_multiple_tokens_wait()
{
Semaphore sem(5);
for (int i = 5; i >= 0; i--) {
bool acquired = sem.try_acquire();
TEST_ASSERT_EQUAL(i > 0, acquired);
}
}
/** Test multiple tokens release
Given a thread and a semaphore initialized with zero tokens and max of 5
when thread calls @a release 6 times on the semaphore
then the token count should be equal to 5 and last release call should fail
*/
void test_multiple_tokens_release()
{
Semaphore sem(0, 5);
for (int i = 5; i > 0; i--) {
osStatus stat = sem.release();
TEST_ASSERT_EQUAL(osOK, stat);
}
osStatus stat = sem.release();
TEST_ASSERT_EQUAL(osErrorResource, stat);
}
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Test 1 token no timeout", test_no_timeout<1>),
Case("Test 0 tokens no timeout", test_no_timeout<0>),
Case("Test multiple tokens wait", test_multiple_tokens_wait),
Case("Test multiple tokens release", test_multiple_tokens_release),
Case("Test semaphore acquire", test_semaphore_acquire),
Case("Test semaphore try acquire", test_semaphore_try_acquire),
Case("Test semaphore try timeout", test_semaphore_try_timeout),
Case("Test semaphore try acquire timeout", test_semaphore_try_acquire_timeout),
#if defined(MBED_CONF_RTOS_PRESENT)
Case("Test single thread", test_single_thread),
Case("Test timeout", test_timeout),
Case("Test multiple threads", test_multi)
#endif
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}
#endif // !DEVICE_USTICKER

View File

@@ -0,0 +1,544 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
using utest::v1::Case;
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#define ALL_SIGNALS 0x7fffffff
#define NO_SIGNALS 0x0
#define SIGNAL1 0x1
#define SIGNAL2 0x2
#define SIGNAL3 0x4
template <int32_t signals, uint32_t timeout, uint32_t test_val>
void run_signal_wait(void)
{
uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout);
TEST_ASSERT_EQUAL(test_val, ret);
}
template <int32_t signals, int32_t test_val>
void run_clear(void)
{
int32_t ret = ThisThread::flags_clear(signals);
TEST_ASSERT_EQUAL(test_val, ret);
}
void run_multiple_wait_clear(int32_t signals1, int32_t signals2, int32_t test_val1, int32_t test_val2)
{
int32_t ret;
ret = ThisThread::flags_clear(signals1);
TEST_ASSERT_EQUAL(test_val1, ret);
ret = ThisThread::flags_clear(signals2);
TEST_ASSERT_EQUAL(test_val2, ret);
}
/** Validate that ticker callback to flags_clr(NO_SIGNALS) doesn't change main thread signals and return actual signals
Given main thread and ticker instance with callback registered
When callback calls @a flags_clear(NO_SIGNALS)
then callback @a flags_clear return status should be ALL_SIGNALS indicating that the signal is unchanged
*/
void test_clear_no_signals_with_ticker(void)
{
Ticker t;
osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS);
t.attach_us([&] { run_multiple_wait_clear(NO_SIGNALS, NO_SIGNALS, ALL_SIGNALS, ALL_SIGNALS); }, 3000);
}
/** Validate all flags_clr clears the signal in one shot
Given main thread and ticker instance with callback registered
When callback calls @a flags_clear(ALL_SIGNALS) with all possible signals
then callback @a flags_clear(NO_SIGNALS) return status should be NO_SIGNALS(0) indicating all signals cleared correctly
*/
void test_clear_all_with_ticker(void)
{
Ticker t;
osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS);
t.attach_us([&] { run_multiple_wait_clear(ALL_SIGNALS, NO_SIGNALS, ALL_SIGNALS, NO_SIGNALS); }, 3000);
}
/** Validate if any signals are set on ticker callback
Given main thread and ticker instance with callback registered
When callback calls @a flags_clear(NO_SIGNALS)
then callback @a flags_clear return status should be NO_SIGNALS(0) indicating no signals set
*/
void test_init_state_with_ticker(void)
{
Ticker t;
t.attach_us(callback(run_clear<NO_SIGNALS, NO_SIGNALS>), 3000);
}
/** Validate signal_wait return status if timeout specified
Given main thread and ticker instance with callback registered
When callback calls @a flags_wait_all_for(signals, timeout) with specified signals and timeout
then callback @a flags_wait_all_for timeout and return 0 indicating no signals set
*/
template <int32_t signals, uint32_t timeout, uint32_t status>
void test_wait_timeout_with_ticker(void)
{
Ticker t;
t.attach_us(callback(run_signal_wait<signals, timeout, status>), 3000);
}
void run_release_wait_signal_wait_callback()
{
osThreadFlagsSet(ThisThread::get_id(), ALL_SIGNALS);
}
/** Validate that call of signal_wait return correctly when thread has all signals already set
Given main thread and ticker instance with callback registered
When main thread @a flags_wait_all_for(ALL_SIGNALS, osWaitForever),
then main thread is blocked
when a callback calls @a osThreadFlagsSet set ALL_SIGNALS
then the main thread is unblocked from @ flags_wait_all_for with the return of ALL_SIGNALS set
*/
void test_wait_all_already_set_with_ticker(void)
{
Ticker t;
t.attach_us([&] { run_release_wait_signal_wait_callback(); }, 3000);
uint32_t ret = ThisThread::flags_wait_all_for(ALL_SIGNALS, osWaitForever);
TEST_ASSERT_EQUAL(ALL_SIGNALS, ret);
}
void run_release_wait_signal_set_callback(int32_t signal, osThreadId_t id)
{
int32_t ret;
if (signal == 0) {
for (int i = 0; i < 16; i++) {
int32_t signal = 1 << i;
ret = osThreadFlagsSet(id, signal);
}
} else {
ret = osThreadFlagsSet(id, signal);
}
}
/** Validate if wait_signal accumulate signals and return correctly when all signals set
Given the main thread and ticker instance with callback registered
When main thread @a flags_wait_all_for,
then main thread is blocked
when a callback calls @a osThreadFlagsSet in a loop to set 16 different signal
then the main thread is unblocked from @ flags_wait_all_for with the return of expected different signals set
*/
void test_wait_all_loop_with_ticker(void)
{
int32_t ret;
Semaphore sem(0, 1);
Ticker t;
osThreadId_t id = ThisThread::get_id();
t.attach_us([&] { run_release_wait_signal_set_callback(0, id); }, 4000);
ret = ThisThread::flags_wait_all_for((ALL_SIGNALS & 0xFFFF), osWaitForever, true);
TEST_ASSERT_EQUAL((ALL_SIGNALS & 0xFFFF), ret);
}
/** Validate if setting same signal twice cause any unwanted behaviour
Given the main thread and two ticker instance with callback registered
When main thread @a flags_wait_all_for,
then main thread is blocked
when a first callback calls @a osThreadFlagsSet set SIGNAL2 and
second callback calls @a osThreadFlagsSet set SIGNAL1 | SIGNAL2 | SIGNAL3 with SIGNAL2 set again
then the main thread is unblocked from @ flags_wait_all_for with return of expected signals set
*/
void test_set_double_with_ticker(void)
{
int32_t ret;
Ticker t1, t2;
osThreadId_t id = ThisThread::get_id();
t1.attach_us([&] { run_release_wait_signal_set_callback(SIGNAL2, id); }, 3000);
t2.attach_us([&] { run_release_wait_signal_set_callback(SIGNAL1 | SIGNAL2 | SIGNAL3, id); }, 4000);
ret = ThisThread::flags_wait_all_for((SIGNAL1 | SIGNAL2 | SIGNAL3), osWaitForever, true);
TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2 | SIGNAL3, ret);
}
#if defined(MBED_CONF_RTOS_PRESENT)
#define TEST_STACK_SIZE 512
#define PROHIBITED_SIGNAL 0x80000000
#define MAX_FLAG_POS 30
struct Sync {
Sync(Semaphore &parent, Semaphore &child): sem_parent(parent), sem_child(child)
{}
Semaphore &sem_parent;
Semaphore &sem_child;
};
template <int32_t signals, uint32_t timeout, uint32_t test_val>
void run_release_signal_wait(Semaphore *sem)
{
sem->release();
uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout);
TEST_ASSERT_EQUAL(test_val, ret);
}
template <int32_t signals, uint32_t timeout, uint32_t test_val>
void run_release_wait_signal_wait(Sync *sync)
{
sync->sem_parent.release();
sync->sem_child.acquire();
uint32_t ret = ThisThread::flags_wait_all_for(signals, timeout);
TEST_ASSERT_EQUAL(test_val, ret);
}
template <int32_t signals, int32_t test_val>
void run_wait_clear(Sync *sync)
{
sync->sem_parent.release();
sync->sem_child.acquire();
int32_t ret = ThisThread::flags_clear(signals);
TEST_ASSERT_EQUAL(test_val, 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.acquire();
int32_t ret = ThisThread::flags_clear(NO_SIGNALS);
TEST_ASSERT_EQUAL(signals, ret);
sync->sem_parent.release();
}
}
template <int32_t signals1, int32_t signals2, int32_t test_val1, int32_t test_val2>
void run_double_wait_clear(Sync *sync)
{
int32_t ret;
sync->sem_parent.release();
sync->sem_child.acquire();
ret = ThisThread::flags_clear(signals1);
TEST_ASSERT_EQUAL(test_val1, ret);
ret = ThisThread::flags_clear(signals2);
TEST_ASSERT_EQUAL(test_val2, ret);
}
/** Validate that call signal_clr(NO_SIGNALS) doesn't change thread signals and return actual signals
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);
Thread t(osPriorityNormal, TEST_STACK_SIZE);
t.start(callback(run_double_wait_clear<NO_SIGNALS, NO_SIGNALS, ALL_SIGNALS, ALL_SIGNALS>, &sync));
sem_parent.acquire();
t.flags_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<NO_SIGNALS, NO_SIGNALS>));
t.join();
}
/** Validate all signals set in one shot
Given two threads A & B are started
When thread A call @a flags_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<NO_SIGNALS, ALL_SIGNALS>, &sync));
sem_parent.acquire();
ret = t.flags_set(ALL_SIGNALS);
TEST_ASSERT_EQUAL(ALL_SIGNALS, ret);
sem_child.release();
t.join();
}
/** Validate that call flags_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 flags_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<NO_SIGNALS, ALL_SIGNALS>, &sync));
sem_parent.acquire();
t.flags_set(ALL_SIGNALS);
#if !MBED_TRAP_ERRORS_ENABLED
ret = t.flags_set(PROHIBITED_SIGNAL);
TEST_ASSERT_EQUAL(osErrorParameter, ret);
#endif
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<ALL_SIGNALS, NO_SIGNALS, ALL_SIGNALS, NO_SIGNALS>, &sync));
sem_parent.acquire();
t.flags_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 flags_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.flags_set(signal);
signals |= signal;
TEST_ASSERT_EQUAL(signals, ret);
sem_child.release();
sem_parent.acquire();
}
t.join();
}
/** Validate signal_wait return status if timeout specified
Given the thread is running
When thread executes @a flags_wait_all_for(signals, timeout) with specified signals and timeout
Then thread @a flags_wait_all_for return should be 0 indicating no flags set
*/
template <int32_t signals, uint32_t timeout, uint32_t status>
void test_wait_timeout(void)
{
Thread t(osPriorityNormal, TEST_STACK_SIZE);
t.start(callback(run_signal_wait<signals, timeout, status>));
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 flags_wait_all_for return immediately with ALL_SIGNALS 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<ALL_SIGNALS, osWaitForever, ALL_SIGNALS>, &sync));
sem_parent.acquire();
TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state());
t.flags_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 flags_set(ALL_SIGNALS) with all possible signals
Then thread B @a flags_wait_all_for return is ALL_SIGNALS 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<ALL_SIGNALS, osWaitForever, ALL_SIGNALS>, &sem));
sem.acquire();
TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state());
t.flags_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 flags_set setting all signals in loop
Then thread B @a flags_wait_all_for return is ALL_SIGNALS 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<ALL_SIGNALS, osWaitForever, ALL_SIGNALS>, &sem));
sem.acquire();
TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state());
for (int i = 0; i < MAX_FLAG_POS; i++) {
int32_t signal = 1 << i;
ret = t.flags_set(signal);
}
ret = t.flags_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 flags_set twice for the same signal
Then thread A @a flags_set status is current signal set
thread B @a flags_wait_all_for return indicates 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 < SIGNAL1 | SIGNAL2 | SIGNAL3, osWaitForever, SIGNAL1 | SIGNAL2 | SIGNAL3 >, &sem));
sem.acquire();
TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state());
ret = t.flags_set(SIGNAL1);
TEST_ASSERT_EQUAL(SIGNAL1, ret);
ret = t.flags_set(SIGNAL2);
TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret);
ret = t.flags_set(SIGNAL2);
TEST_ASSERT_EQUAL(SIGNAL1 | SIGNAL2, ret);
TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state());
ret = t.flags_set(SIGNAL3);
TEST_ASSERT_EQUAL(NO_SIGNALS, ret);
t.join();
}
#endif
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(20, "default_auto");
return utest::v1::verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Validate that ticker callback flags_clear(NO_SIGNALS) doesn't change main thread flags and return actual flags", test_clear_no_signals_with_ticker),
Case("Validate if any flags are set on ticker callback", test_init_state_with_ticker),
Case("Validate all flags clear in one shot using ticker callback", test_clear_all_with_ticker),
Case("Validate ticker callback flags_wait return status if timeout specified: 0[ms] no flags", test_wait_timeout_with_ticker<0, 0, 0>),
Case("Validate ticker callback flags_wait return status if timeout specified: 0[ms] all flags", test_wait_timeout_with_ticker<ALL_SIGNALS, 0, 0>),
Case("Validate ticker callback flags_wait return status if timeout specified: 1[ms] no flags", test_wait_timeout_with_ticker<0, 1, 0>),
Case("Validate ticker callback flags_wait return status if timeout specified: 1[ms] all flags", test_wait_timeout_with_ticker<ALL_SIGNALS, 1, 0>),
Case("Validate that main thread call of flags_wait_all_for return correctly when ticker callback set all flags", test_wait_all_already_set_with_ticker),
Case("Validate if setting same flag twice cause any unwanted behaviour when ticker callbacks set", test_set_double_with_ticker),
Case("Validate if main thread flags_wait_all_for accumulate flags and return correctly when all flags set by ticker callback", test_wait_all_loop_with_ticker),
#if defined(MBED_CONF_RTOS_PRESENT)
Case("Validate that call flags_clear(NO_SIGNALS) doesn't change thread flags and return actual flags", test_clear_no_signals),
Case("Validate if any flags are set on just created thread", test_init_state),
Case("Validate all flags set in one shot", test_set_all),
Case("Validate that call flags_set with prohibited flag doesn't change thread flags", test_set_prohibited),
Case("Validate all flags clear in one shot", test_clear_all),
Case("Validate all flags set one by one in loop", test_set_all_loop),
Case("Validate flags_wait return status if timeout specified: 0[ms] no flags", test_wait_timeout<0, 0, 0>),
Case("Validate flags_wait return status if timeout specified: 0[ms] all flags", test_wait_timeout<ALL_SIGNALS, 0, 0>),
Case("Validate flags_wait return status if timeout specified: 1[ms] no flags", test_wait_timeout<0, 1, 0>),
Case("Validate flags_wait return status if timeout specified: 1[ms] all flags", test_wait_timeout<ALL_SIGNALS, 1, 0>),
Case("Validate that call of flags_wait_all_for return correctly when thread has all flags already set", test_wait_all_already_set),
Case("Validate if flags_wait_all_for return correctly when all flags set", test_wait_all),
Case("Validate if flags_wait_all_for accumulate flags and return correctly when all flags set", test_wait_all_loop),
Case("Validate if setting same flag twice cause any unwanted behaviour", test_set_double),
#endif
};
utest::v1::Specification specification(test_setup, cases);
int main()
{
return !utest::v1::Harness::run(specification);
}
#endif // !DEVICE_USTICKER

View File

@@ -0,0 +1,376 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "ticker_api.h"
#include "platform/source/SysTimer.h"
using namespace std::chrono;
#define TEST_TICKS 42
#define TEST_TICK_PERIOD std::ratio<TEST_TICKS, 1000>::type
#define TEST_TICK_DURATION duration<long long, TEST_TICK_PERIOD>
#define DELAY TEST_TICK_DURATION(1)
#define DELAY_DELTA 2500us
#define TEST_ASSERT_EQUAL_DURATION(expected, actual) \
do { \
using ct = std::common_type_t<decltype(expected), decltype(actual)>; \
TEST_ASSERT_EQUAL(ct(expected).count(), ct(actual).count()); \
} while (0)
#define TEST_ASSERT_EQUAL_TIME_POINT(expected, actual) \
do { \
using ct = std::common_type_t<decltype(expected), decltype(actual)>; \
TEST_ASSERT_EQUAL(ct(expected).time_since_epoch().count(), ct(actual).time_since_epoch().count()); \
} while (0)
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
/* Use a specific delta value for deep sleep, as entry/exit adds up extra latency.
* Use deep sleep latency if defined and add 1ms extra delta */
#if defined MBED_CONF_TARGET_DEEP_SLEEP_LATENCY
#define DEEP_SLEEP_DELAY_DELTA milliseconds(MBED_CONF_TARGET_DEEP_SLEEP_LATENCY + 1)
#else
#define DEEP_SLEEP_DELAY_DELTA 2500us
#endif
using namespace utest::v1;
using mbed::internal::SysTimer;
// The SysTick interrupt must not be set as pending by the test code.
template <class Period>
class SysTimerTest: public SysTimer<Period, false> {
private:
Semaphore _sem;
virtual void handler()
{
_sem.release();
SysTimer<Period, false>::handler();
}
public:
SysTimerTest() :
SysTimer<Period, false>(), _sem(0, 1)
{
}
SysTimerTest(const ticker_data_t *data) :
SysTimer<Period, false>(data), _sem(0, 1)
{
}
virtual ~SysTimerTest()
{
}
bool sem_try_acquire(rtos::Kernel::Clock::duration_u32 millisec)
{
return _sem.try_acquire_for(millisec);
}
void sem_acquire()
{
_sem.acquire();
}
};
duration<timestamp_t, std::micro> mock_ticker_timestamp;
void mock_ticker_init()
{
}
uint32_t mock_ticker_read()
{
return mock_ticker_timestamp.count();
}
void mock_ticker_disable_interrupt()
{
}
void mock_ticker_clear_interrupt()
{
}
void mock_ticker_set_interrupt(timestamp_t timestamp)
{
}
void mock_ticker_fire_interrupt()
{
}
void mock_ticker_free()
{
}
const ticker_info_t *mock_ticker_get_info()
{
static const ticker_info_t mock_ticker_info = {
.frequency = 1000000,
.bits = 32
};
return &mock_ticker_info;
}
ticker_interface_t mock_ticker_interface = {
.init = mock_ticker_init,
.read = mock_ticker_read,
.disable_interrupt = mock_ticker_disable_interrupt,
.clear_interrupt = mock_ticker_clear_interrupt,
.set_interrupt = mock_ticker_set_interrupt,
.fire_interrupt = mock_ticker_fire_interrupt,
.free = mock_ticker_free,
.get_info = mock_ticker_get_info,
};
ticker_event_queue_t mock_ticker_event_queue;
const ticker_data_t mock_ticker_data = {
.interface = &mock_ticker_interface,
.queue = &mock_ticker_event_queue
};
void mock_ticker_reset()
{
mock_ticker_timestamp = 0s;
memset(&mock_ticker_event_queue, 0, sizeof mock_ticker_event_queue);
}
/** Test tick count is zero upon creation
*
* Given a SysTimer
* When the timer is created
* Then tick count is zero
*/
void test_created_with_zero_tick_count(void)
{
SysTimerTest<std::milli> st;
using time_point = decltype(st)::time_point;
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick());
}
/** Test tick count is updated correctly
*
* Given a SysTimer
* When the @a suspend and @a resume methods are called immediately after creation
* Then the tick count is not updated
* When @a suspend and @a resume methods are called again after a delay
* Then the tick count is updated
* and the number of ticks incremented is equal TEST_TICKS
* When @a suspend and @a resume methods are called again without a delay
* Then the tick count is not updated
*/
void test_update_tick(void)
{
mock_ticker_reset();
SysTimerTest<std::milli> st(&mock_ticker_data);
using time_point = decltype(st)::time_point;
st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2));
st.cancel_wake();
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick());
st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2));
mock_ticker_timestamp = TEST_TICK_DURATION(1);
st.cancel_wake();
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.update_and_get_tick());
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick());
st.set_wake_time(st.get_tick() + TEST_TICK_DURATION(2));
st.cancel_wake();
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick());
}
/** Test get_time returns correct time
*
* Given a SysTimer
* When @a get_time method is called before and after a delay
* Then time difference is equal the delay
*/
void test_get_time(void)
{
mock_ticker_reset();
SysTimerTest<std::milli> st(&mock_ticker_data);
auto t1 = st.get_time();
mock_ticker_timestamp = TEST_TICK_DURATION(1);
auto t2 = st.get_time();
TEST_ASSERT_EQUAL_DURATION(TEST_TICK_DURATION(1), t2 - t1);
}
/** Test cancel_tick
*
* Given a SysTimer with a scheduled tick
* When @a cancel_tick is called before the given number of ticks elapse
* Then the handler is never called
* and the tick count is not incremented
*/
void test_cancel_tick(void)
{
SysTimerTest<TEST_TICK_PERIOD> st;
using time_point = decltype(st)::time_point;
st.cancel_tick();
st.start_tick();
st.cancel_tick();
bool acquired = st.sem_try_acquire(duration_cast<Kernel::Clock::duration>(TEST_TICK_DURATION(1) + DELAY_DELTA));
TEST_ASSERT_FALSE(acquired);
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(0)), st.get_tick());
}
/** Test handler called twice
*
* Given a SysTimer with a tick scheduled with delta = TEST_TICKS
* When the handler is called
* Then the tick count is incremented by 1
* and elapsed time is equal 1000000ULL * TEST_TICKS / OS_TICK_FREQ;
* When more time elapses
* Repeat a second time.
*/
void test_handler_called_twice(void)
{
SysTimerTest<TEST_TICK_PERIOD> st;
using time_point = decltype(st)::time_point;
auto t1 = st.get_time();
bool acquired = st.sem_try_acquire(0s);
TEST_ASSERT_FALSE(acquired);
st.start_tick();
// Wait in a busy loop to prevent entering sleep or deepsleep modes.
do {
acquired = st.sem_try_acquire(0s);
} while (!acquired);
auto t2 = st.get_time();
TEST_ASSERT_TRUE(acquired);
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(1)), st.get_tick());
TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(1), t2 - t1);
// Wait in a busy loop to prevent entering sleep or deepsleep modes.
do {
acquired = st.sem_try_acquire(0s);
} while (!acquired);
t2 = st.get_time();
TEST_ASSERT_TRUE(acquired);
TEST_ASSERT_EQUAL_TIME_POINT(time_point(TEST_TICK_DURATION(2)), st.get_tick());
TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(2), t2 - t1);
st.cancel_tick();
}
#if DEVICE_SLEEP
/** Test wake up from sleep
*
* Given a SysTimer with a tick scheduled in the future
* and a core in sleep mode
* When given time elapses
* Then the uC is woken up from sleep
* and the tick handler is called
* and measured time matches requested delay
*/
void test_sleep(void)
{
Timer timer;
SysTimerTest<TEST_TICK_PERIOD> st;
sleep_manager_lock_deep_sleep();
timer.start();
st.start_tick();
TEST_ASSERT_FALSE_MESSAGE(sleep_manager_can_deep_sleep(), "Deep sleep should be disallowed");
st.sem_acquire();
timer.stop();
st.cancel_tick();
sleep_manager_unlock_deep_sleep();
TEST_ASSERT_DURATION_WITHIN(DELAY_DELTA, TEST_TICK_DURATION(1), timer.elapsed_time());
}
#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER
/** Test wake up from deepsleep
*
* Given a SysTimer with a tick scheduled in the future
* and a core in deepsleep mode
* When given time elapses
* Then the uC is woken up from deepsleep
* and the tick handler is called
* and measured time matches requested delay
*/
void test_deepsleep(void)
{
/*
* Since deepsleep() may shut down the UART peripheral, we wait for 10ms
* to allow for hardware serial buffers to completely flush.
* This should be replaced with a better function that checks if the
* hardware buffers are empty. However, such an API does not exist now,
* so we'll use the ThisThread::sleep_for() function for now.
*/
ThisThread::sleep_for(10ms);
// Regular Timer might be disabled during deepsleep.
LowPowerTimer lptimer;
SysTimerTest<TEST_TICK_PERIOD> st;
lptimer.start();
st.start_tick();
TEST_ASSERT_TRUE_MESSAGE(sleep_manager_can_deep_sleep_test_check(), "Deep sleep should be allowed");
st.sem_acquire();
lptimer.stop();
st.cancel_tick();
TEST_ASSERT_DURATION_WITHIN(DEEP_SLEEP_DELAY_DELTA, TEST_TICK_DURATION(1), lptimer.elapsed_time());
}
#endif
#endif
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(15, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
Case cases[] = {
Case("Tick count is zero upon creation", test_created_with_zero_tick_count),
Case("Tick count is updated correctly", test_update_tick),
Case("Time is updated correctly", test_get_time),
Case("Tick can be cancelled", test_cancel_tick),
Case("Handler called twice", test_handler_called_twice),
#if DEVICE_SLEEP
Case("Wake up from sleep", test_sleep),
#if DEVICE_LPTICKER && !MBED_CONF_TARGET_TICKLESS_FROM_US_TICKER
Case("Wake up from deep sleep", test_deepsleep),
#endif
#endif
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2013-2016, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL
#define MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL
#include <rtos.h>
#include <mstd_mutex>
using mstd::lock_guard;
/**
* Thread safe wrapper for integral types.
* @tparam T type of the integral
*/
template<typename T>
class SynchronizedIntegral {
public:
SynchronizedIntegral(T value) : _mutex(), _value(value) { }
// preincrement operator
T operator++()
{
lock_guard<rtos::Mutex> lock(_mutex);
return ++_value;
}
// predecrement operator
T operator--()
{
lock_guard<rtos::Mutex> lock(_mutex);
return --_value;
}
// post increment operator
T operator++(int)
{
lock_guard<rtos::Mutex> lock(_mutex);
return _value++;
}
// post decrement operator
T operator--(int)
{
lock_guard<rtos::Mutex> lock(_mutex);
return _value--;
}
// conversion operator, used also for <,>,<=,>=,== and !=
operator T() const
{
lock_guard<rtos::Mutex> lock(_mutex);
return _value;
}
// access to the internal mutex
rtos::Mutex &internal_mutex()
{
return _mutex;
}
private:
mutable rtos::Mutex _mutex;
T _value;
};
#endif /* MBEDMICRO_RTOS_MBED_THREADS_SYNCHRONIZED_INTEGRAL */

View File

@@ -0,0 +1,863 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)
#error [NOT_SUPPORTED] Threads test cases require RTOS with multithread to run
#else
#if !DEVICE_USTICKER
#error [NOT_SUPPORTED] UsTicker need to be enabled for this test.
#else
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"
#include "SynchronizedIntegral.h"
#include <mstd_mutex>
#include <type_traits>
#define THREAD_STACK_SIZE 512
#if defined(__CORTEX_A9) || defined(__CORTEX_M23) || defined(__CORTEX_M33) || defined(TARGET_ARM_FM) || defined(TARGET_CY8CKIT_062_WIFI_BT_PSA)
#define PARALLEL_THREAD_STACK_SIZE 512
#define CHILD_THREAD_STACK_SIZE 512
#else
#define PARALLEL_THREAD_STACK_SIZE 384
#define CHILD_THREAD_STACK_SIZE 384
#endif
#define TEST_ASSERT_DURATION_WITHIN(delta, expected, actual) \
do { \
using ct = std::common_type_t<decltype(delta), decltype(expected), decltype(actual)>; \
TEST_ASSERT_INT_WITHIN(ct(delta).count(), ct(expected).count(), ct(actual).count()); \
} while (0)
using namespace utest::v1;
using mstd::lock_guard;
// The counter type used accross all the tests
// It is internall ysynchronized so read
typedef SynchronizedIntegral<int> counter_t;
template<osPriority P, uint32_t S>
class ParallelThread : public Thread {
public:
ParallelThread() : Thread(P, S) { }
};
// Tasks with different functions to test on threads
void increment(counter_t *counter)
{
(*counter)++;
}
void increment_with_yield(counter_t *counter)
{
ThisThread::yield();
(*counter)++;
}
void increment_with_wait(counter_t *counter)
{
ThisThread::sleep_for(100ms);
(*counter)++;
}
void increment_with_child(counter_t *counter)
{
Thread *child = new (std::nothrow) Thread(osPriorityNormal, CHILD_THREAD_STACK_SIZE);
char *dummy = new (std::nothrow) char[CHILD_THREAD_STACK_SIZE];
delete[] dummy;
// Don't fail test due to lack of memory. Call function directly instead
if (!child || !dummy) {
increment(counter);
delete child;
return;
}
child->start(callback(increment, counter));
child->join();
delete child;
}
void increment_with_murder(counter_t *counter)
{
{
// take ownership of the counter mutex so it prevent the child to
// modify counter.
lock_guard<rtos::Mutex> lock(counter->internal_mutex());
Thread *child = new (std::nothrow) Thread(osPriorityNormal, CHILD_THREAD_STACK_SIZE);
char *dummy = new (std::nothrow) char[CHILD_THREAD_STACK_SIZE];
delete[] dummy;
// Don't fail test due to lack of memory.
if (!child || !dummy) {
delete child;
goto end;
}
child->start(callback(increment, counter));
child->terminate();
delete child;
}
end:
(*counter)++;
}
void self_terminate(Thread *self)
{
self->terminate();
// Code should not get here
TEST_ASSERT(0);
}
// Tests that spawn tasks in different configurations
/** Template for tests: single thread, with yield, with wait, with child, with murder
Testing single thread
Given single thread is started
when the thread increments the counter
then the final value of the counter is equal to 1
Testing single thread with yield
Given single thread is started
when the thread yields and then increments the counter
then the final value of the counter is equal to 1
Testing single thread with wait
Given single thread is started
when the thread waits for 100ms and then increments the counter
then the final value of the counter is equal to 1
Testing single thread with child
Given single thread is started
when the thread spawns another thread that increments the counter
then the final value of the counter is equal to 1
Testing serial threads with murder
Given single thread is started
when the parent thread is holding a lock
and the parent thread spawns a child thread that waits for the lock before incrementing the counter
and the parent terminates the child before releasing the lock
and the parent increments the counter
then the final value of the counter is equal to 1
*/
template <void (*F)(counter_t *)>
void test_single_thread()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
counter_t counter(0);
Thread thread(osPriorityNormal, THREAD_STACK_SIZE);
thread.start(callback(F, &counter));
thread.join();
TEST_ASSERT_EQUAL(counter, 1);
}
/** Template for tests: parallel threads, with yield, with wait, with child, with murder
Testing parallel threads
Given multiple threads are started in parallel
when each of the threads increments the counter
then the final value of the counter is equal to number of threads
Testing parallel threads with yield
Given multiple threads are started in parallel
when each of the threads yields and then increments the counter
then the final value of the counter is equal to number of threads
Testing parallel threads with wait
Given multiple threads are started in parallel
when each of the threads waits for 100ms and then increments the counter
then the final value of the counter is equal to number of threads
Testing parallel threads with child
Given multiple threads are started in parallel
when each of the threads spawns another thread that increments the counter
then the final value of the counter is equal to number of parallel threads
Testing parallel threads with murder
Given multiple threads are started in parallel
when the parent thread is holding a lock
and the parent thread spawns a child thread that waits for the lock before incrementing the counter
and the parent terminates the child before releasing the lock
and the parent increments the counter
then the final value of the counter is equal to number of parallel threads
*/
template <int N, void (*F)(counter_t *)>
void test_parallel_threads()
{
char *dummy = new (std::nothrow) char[PARALLEL_THREAD_STACK_SIZE * N];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
counter_t counter(0);
ParallelThread<osPriorityNormal, PARALLEL_THREAD_STACK_SIZE> threads[N];
for (int i = 0; i < N; i++) {
threads[i].start(callback(F, &counter));
}
for (int i = 0; i < N; i++) {
threads[i].join();
}
TEST_ASSERT_EQUAL(counter, N);
}
/** Template for tests: serial threads, with yield, with wait, with child, with murder
Testing serial threads
Given multiple threads are started serially
when each of the threads increments the counter
then the final value of the counter is equal to number of threads
Testing serial threads with yield
Given multiple threads are started serially
when each of the threads yields and then increments the counter
then the final value of the counter is equal to number of threads
Testing serial threads with wait
Given multiple threads are started serially
when each of the threads waits for 100ms and then increments the counter
then the final value of the counter is equal to number of threads
Testing serial threads with child
Given multiple threads are started serially
when each of the threads spawns another thread that increments the counter
then the final value of the counter is equal to number of serial threads
Testing serial threads with murder
Given multiple threads are started serially
when the parent thread is holding a lock
and the parent thread spawns a child thread that waits for the lock before incrementing the counter
and the parent terminates the child before releasing the lock
and the parent increments the counter
then the final value of the counter is equal to number of serial threads
*/
template <int N, void (*F)(counter_t *)>
void test_serial_threads()
{
counter_t counter(0);
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
for (int i = 0; i < N; i++) {
Thread thread(osPriorityNormal, THREAD_STACK_SIZE);
thread.start(callback(F, &counter));
thread.join();
}
TEST_ASSERT_EQUAL(counter, N);
}
/** Testing thread self terminate
Given the thread is running
when the thread calls @a terminate on its self
then the thread terminates execution cleanly
*/
void test_self_terminate()
{
Thread *thread = new (std::nothrow) Thread(osPriorityNormal, THREAD_STACK_SIZE);
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
// Don't fail test due to lack of memory.
if (!thread || !dummy) {
goto end;
}
thread->start(callback(self_terminate, thread));
thread->join();
end:
delete thread;
}
void flags_wait()
{
uint32_t flags = ThisThread::flags_wait_all(0x1);
TEST_ASSERT_EQUAL(0x1, flags);
}
void flags_wait_tout()
{
uint32_t flags = ThisThread::flags_wait_all_for(0x2, 50ms);
TEST_ASSERT_EQUAL(0x1, flags);
}
void flags_wait_multibit()
{
uint32_t flags = ThisThread::flags_wait_all(0x1 | 0x2);
TEST_ASSERT_EQUAL(0x3, flags);
}
void flags_wait_multibit_any()
{
uint32_t flags = ThisThread::flags_wait_any(0x1 | 0x2);
TEST_ASSERT_NOT_EQUAL(0x0, flags);
}
void flags_wait_multibit_tout()
{
uint32_t flags = ThisThread::flags_wait_all_for(0x1 | 0x2, 50ms);
TEST_ASSERT_NOT_EQUAL(0x3, flags);
}
/**
Testing thread flags: wait
Given two threads (A & B) are started
when thread A executes @a flags_wait_all(0x1)
and thread B execute @a flags_set(0x1)
then thread A exits the wait and continues execution
Testing thread flags: timeout
Given two threads (A & B) are started
when thread A executes @a flags_wait_all_for(0x1 | 0x2, 50) with a timeout of 50ms
and thread B execute @a flags_set(0x2)
then thread A keeps waiting for correct flags until it timeouts
Testing thread flags: multi-bit
Given two threads (A & B) are started
when thread A executes @a flags_wait_all(0x1 | 0x2)
and thread B execute @a flags_set(0x1 | 0x2)
then thread A exits the wait and continues execution
Testing thread flags: multi-bit any
Given two threads (A & B) are started
when thread A executes @a flags_wait_any(0x1 | 0x2)
and thread B execute @a flags_set(0x1)
then thread A exits the wait and continues execution
Testing thread flags: multi-bit timeout
Given two threads (A & B) are started
when thread A executes @a flags_wait_all_for(0x1, 50) with a timeout of 50ms
and thread B execute @a flags_set(0x2)
then thread A keeps waiting for correct flags until it timeouts
*/
template <int S, void (*F)()>
void test_thread_flags_set()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t_wait(osPriorityNormal, THREAD_STACK_SIZE);
t_wait.start(callback(F));
ThisThread::yield();
Thread::State state = t_wait.get_state();
TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, state);
int32_t res = t_wait.flags_set(S);
t_wait.join();
}
void flags_clear()
{
ThisThread::yield();
int32_t sig = ThisThread::flags_clear(0x1);
TEST_ASSERT_EQUAL(0x1, sig);
/* Flags cleared we should get timeout */
uint32_t flags = ThisThread::flags_wait_all_for(0x1, 0s);
TEST_ASSERT_EQUAL(0, flags);
}
/** Testing thread flags: flags clear
Given two threads (A & B) are started
when thread A executes @a flags_set(0x1)
and thread B execute @a flags_clear(0x1)
and thread B execute @a flags_wait_all_for(0x1, 0)
then thread B @a flags_wait_all_for return should be 0 indicating no flags set
*/
void test_thread_flags_clear()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t_wait(osPriorityNormal, THREAD_STACK_SIZE);
t_wait.start(callback(flags_clear));
int32_t res = t_wait.flags_set(0x1);
TEST_ASSERT_EQUAL(0x1, res);
t_wait.join();
}
void thread_wait_flags()
{
ThisThread::flags_wait_all(0x1);
}
void stack_info()
{
ThisThread::flags_wait_all(0x1);
thread_wait_flags();
ThisThread::flags_wait_all(0x1);
}
/** Testing thread stack info
Given the thread is running
when a function is called from the thread context
then the stack usage goes up
and the reported stack size is as requested in the constructor
and the sum of free and used stack sizes is equal to the total stack size
when the function returns
then the stack usage goes down
and the reported stack size is as requested in the constructor
and the sum of free and used stack sizes is equal to the total stack size
*/
void test_thread_stack_info()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
t.start(callback(stack_info));
ThisThread::yield();
TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.stack_size());
TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack());
uint32_t last_stack = t.used_stack();
t.flags_set(0x1);
ThisThread::yield();
TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack());
TEST_ASSERT(last_stack <= t.used_stack());
last_stack = t.used_stack();
t.flags_set(0x1);
ThisThread::yield();
TEST_ASSERT_EQUAL(THREAD_STACK_SIZE, t.free_stack() + t.used_stack());
TEST_ASSERT(last_stack >= t.used_stack());
t.flags_set(0x1);
t.join();
}
/** Testing thread wait aka delay
Given the thread is running
when the @a wait function is called
then the thread sleeps for given amount of time
*/
void test_thread_wait()
{
Timer timer;
timer.start();
ThisThread::sleep_for(150ms);
TEST_ASSERT_DURATION_WITHIN(50ms, 150ms, timer.elapsed_time());
}
/** Testing thread name
Given a thread is started with a specified name
when the name is queried using @a get_name
then the returned name is as set
*/
void test_thread_name()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
const char tname[] = "Amazing thread";
Thread t(osPriorityNormal, THREAD_STACK_SIZE, NULL, tname);
TEST_ASSERT_EQUAL(strcmp(tname, t.get_name()), 0);
t.start([&] { TEST_ASSERT_EQUAL(strcmp(tname, ThisThread::get_name()), 0); });
t.join();
}
void test_deleted_thread()
{
}
/** Testing thread states: deleted
Given the thread is not started
then its state, as reported by @a get_state, is @a Deleted
when the thread is started and finishes executing
then its state, as reported by @a get_state, is @a Deleted
*/
void test_deleted()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state());
t.start(callback(test_deleted_thread));
t.join();
TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state());
}
void test_delay_thread()
{
ThisThread::sleep_for(50ms);
}
/** Testing thread states: wait delay
Given the thread is running
when thread calls @a wait
then its state, as reported by @a get_state, is @a WaitingDelay
*/
void test_delay()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
t.start(callback(test_delay_thread));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingDelay, t.get_state());
t.join();
TEST_ASSERT_EQUAL(Thread::Deleted, t.get_state());
}
void test_thread_flags_thread()
{
ThisThread::flags_wait_all(0x1);
}
/** Testing thread states: wait flags
Given the thread is running
when thread waits for flags
then its state, as reported by @a get_state, is @a WaitingThreadFlag
*/
void test_thread_flags()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
t.start(callback(test_thread_flags_thread));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingThreadFlag, t.get_state());
t.flags_set(0x1);
}
void test_evt_flag_thread(EventFlags *evtflg)
{
evtflg->wait_any(0x1);
}
/** Testing thread states: wait evt flag
Given the thread is running
when thread waits for an event flag
then its state, as reported by @a get_state, is @a WaitingEventFlag
*/
void test_evt_flag()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
EventFlags evtflg;
t.start(callback(test_evt_flag_thread, &evtflg));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingEventFlag, t.get_state());
evtflg.set(0x1);
}
void test_mutex_thread(Mutex *mutex)
{
mutex->lock();
}
/** Testing thread states: wait mutex
Given the thread is running
when thread waits for a mutex
then its state, as reported by @a get_state, is @a WaitingMutex
*/
void test_mutex()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Mutex mutex;
mutex.lock();
t.start(callback(test_mutex_thread, &mutex));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingMutex, t.get_state());
mutex.unlock();
}
void test_semaphore_thread(Semaphore *sem)
{
sem->acquire();
}
/** Testing thread states: wait semaphore
Given the thread is running
when thread waits for a semaphore
then its state, as reported by @a get_state, is @a WaitingSemaphore
*/
void test_semaphore()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Semaphore sem;
t.start(callback(test_semaphore_thread, &sem));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingSemaphore, t.get_state());
sem.release();
}
void test_msg_get_thread(Queue<int32_t, 1> *queue)
{
queue->get();
}
/** Testing thread states: wait message get
Given the thread is running
when thread tries to get a message from an empty queue
then its state, as reported by @a get_state, is @a WaitingMessageGet
*/
void test_msg_get()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Queue<int32_t, 1> queue;
t.start(callback(test_msg_get_thread, &queue));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingMessageGet, t.get_state());
queue.try_put((int32_t *)0xE1EE7);
}
void test_msg_put_thread(Queue<int32_t, 1> *queue)
{
queue->try_put_for(Kernel::wait_for_u32_forever, (int32_t *)0xDEADBEEF);
}
/** Testing thread states: wait message put
Given the thread is running
when thread tries to put a message into a full queue
then its state, as reported by @a get_state, is @a WaitingMessagePut
*/
void test_msg_put()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
Queue<int32_t, 1> queue;
queue.try_put((int32_t *)0xE1EE7);
t.start(callback(test_msg_put_thread, &queue));
ThisThread::yield();
TEST_ASSERT_EQUAL(Thread::WaitingMessagePut, t.get_state());
queue.get();
}
/** Utility function that places some date on the stack */
void use_some_stack()
{
volatile uint32_t stack_filler[10] = {0xDEADBEEF};
}
/** Testing thread with external stack memory
Given external buffer is supplied as stack to a thread
when the thread executes
then the supplies buffer is used as a stack
*/
void test_thread_ext_stack()
{
char stack[512];
Thread t(osPriorityNormal, sizeof(stack), (unsigned char *)stack);
memset(&stack, 0, sizeof(stack));
t.start(callback(use_some_stack));
t.join();
/* If buffer was used as a stack it was cleared with pattern and some data were placed in it */
for (unsigned i = 0; i < sizeof(stack); i++) {
if (stack[i] != 0) {
return;
}
}
TEST_FAIL_MESSAGE("External stack was not used.");
}
/** Testing thread priority operations
Given thread running with osPriorityNormal
when new priority is set using @a set_priority
then priority is changed and can be retrieved using @a get_priority
*/
void test_thread_prio()
{
char *dummy = new (std::nothrow) char[THREAD_STACK_SIZE];
delete[] dummy;
TEST_SKIP_UNLESS_MESSAGE(dummy, "Not enough memory to run test");
Thread t(osPriorityNormal, THREAD_STACK_SIZE);
t.start(callback(thread_wait_flags));
TEST_ASSERT_EQUAL(osPriorityNormal, t.get_priority());
t.set_priority(osPriorityHigh);
TEST_ASSERT_EQUAL(osPriorityHigh, t.get_priority());
t.flags_set(0x1);
t.join();
}
utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(25, "default_auto");
return verbose_test_setup_handler(number_of_cases);
}
#define DEFAULT_HANDLERS NULL,NULL,greentea_case_setup_handler,greentea_case_teardown_handler,greentea_case_failure_abort_handler
// Test cases. It's spelled out rather than constructed with macro because
// macros don't play nicely with the templates (extra comma).
static const case_t cases[] = {
{"Testing single thread", test_single_thread<increment>, DEFAULT_HANDLERS},
{"Testing parallel threads", test_parallel_threads<3, increment>, DEFAULT_HANDLERS},
{"Testing serial threads", test_serial_threads<10, increment>, DEFAULT_HANDLERS},
{"Testing single thread with yield", test_single_thread<increment_with_yield>, DEFAULT_HANDLERS},
{"Testing parallel threads with yield", test_parallel_threads<3, increment_with_yield>, DEFAULT_HANDLERS},
{"Testing serial threads with yield", test_serial_threads<10, increment_with_yield>, DEFAULT_HANDLERS},
{"Testing single thread with wait", test_single_thread<increment_with_wait>, DEFAULT_HANDLERS},
{"Testing parallel threads with wait", test_parallel_threads<3, increment_with_wait>, DEFAULT_HANDLERS},
{"Testing serial threads with wait", test_serial_threads<10, increment_with_wait>, DEFAULT_HANDLERS},
{"Testing single thread with child", test_single_thread<increment_with_child>, DEFAULT_HANDLERS},
{"Testing parallel threads with child", test_parallel_threads<2, increment_with_child>, DEFAULT_HANDLERS},
{"Testing serial threads with child", test_serial_threads<10, increment_with_child>, DEFAULT_HANDLERS},
{"Testing single thread with murder", test_single_thread<increment_with_murder>, DEFAULT_HANDLERS},
{"Testing parallel threads with murder", test_parallel_threads<2, increment_with_murder>, DEFAULT_HANDLERS},
{"Testing serial threads with murder", test_serial_threads<10, increment_with_murder>, DEFAULT_HANDLERS},
{"Testing thread self terminate", test_self_terminate, DEFAULT_HANDLERS},
{"Testing thread flags: wait", test_thread_flags_set<0x1, flags_wait>, DEFAULT_HANDLERS},
{"Testing thread flags: timeout", test_thread_flags_set<0x1, flags_wait_tout>, DEFAULT_HANDLERS},
{"Testing thread flags: multi-bit all", test_thread_flags_set<0x3, flags_wait_multibit>, DEFAULT_HANDLERS},
{"Testing thread flags: multi-bit all timeout", test_thread_flags_set<0x1, flags_wait_multibit_tout>, DEFAULT_HANDLERS},
{"Testing thread flags: multi-bit any", test_thread_flags_set<0x1, flags_wait_multibit_any>, DEFAULT_HANDLERS},
{"Testing thread flags: flags clear", test_thread_flags_clear, DEFAULT_HANDLERS},
{"Testing thread stack info", test_thread_stack_info, DEFAULT_HANDLERS},
{"Testing thread wait", test_thread_wait, DEFAULT_HANDLERS},
{"Testing thread name", test_thread_name, DEFAULT_HANDLERS},
{"Testing thread states: deleted", test_deleted, DEFAULT_HANDLERS},
{"Testing thread states: wait delay", test_delay, DEFAULT_HANDLERS},
{"Testing thread states: wait thread flags", test_thread_flags, DEFAULT_HANDLERS},
{"Testing thread states: wait event flag", test_evt_flag, DEFAULT_HANDLERS},
{"Testing thread states: wait mutex", test_mutex, DEFAULT_HANDLERS},
{"Testing thread states: wait semaphore", test_semaphore, DEFAULT_HANDLERS},
{"Testing thread states: wait message get", test_msg_get, DEFAULT_HANDLERS},
{"Testing thread states: wait message put", test_msg_put, DEFAULT_HANDLERS},
{"Testing thread with external stack memory", test_thread_ext_stack, DEFAULT_HANDLERS},
{"Testing thread priority ops", test_thread_prio, DEFAULT_HANDLERS}
};
Specification specification(test_setup, cases);
int main()
{
return !Harness::run(specification);
}
#endif // !DEVICE_USTICKER
#endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT)