Import Mbed OS hard-float snapshot
This commit is contained in:
688
rtos/tests/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp
Normal file
688
rtos/tests/TESTS/mbedmicro-rtos-mbed/MemoryPool/main.cpp
Normal 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)
|
||||
130
rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp
Normal file
130
rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp
Normal 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)
|
||||
195
rtos/tests/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp
Normal file
195
rtos/tests/TESTS/mbedmicro-rtos-mbed/condition_variable/main.cpp
Normal 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)
|
||||
452
rtos/tests/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp
Normal file
452
rtos/tests/TESTS/mbedmicro-rtos-mbed/event_flags/main.cpp
Normal 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
|
||||
293
rtos/tests/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp
Normal file
293
rtos/tests/TESTS/mbedmicro-rtos-mbed/heap_and_stack/main.cpp
Normal 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
|
||||
135
rtos/tests/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp
Normal file
135
rtos/tests/TESTS/mbedmicro-rtos-mbed/kernel_tick_count/main.cpp
Normal 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);
|
||||
}
|
||||
510
rtos/tests/TESTS/mbedmicro-rtos-mbed/mail/main.cpp
Normal file
510
rtos/tests/TESTS/mbedmicro-rtos-mbed/mail/main.cpp
Normal 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)
|
||||
225
rtos/tests/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp
Normal file
225
rtos/tests/TESTS/mbedmicro-rtos-mbed/malloc/main.cpp
Normal 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
|
||||
323
rtos/tests/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp
Normal file
323
rtos/tests/TESTS/mbedmicro-rtos-mbed/mutex/main.cpp
Normal 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)
|
||||
347
rtos/tests/TESTS/mbedmicro-rtos-mbed/queue/main.cpp
Normal file
347
rtos/tests/TESTS/mbedmicro-rtos-mbed/queue/main.cpp
Normal 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)
|
||||
359
rtos/tests/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp
Normal file
359
rtos/tests/TESTS/mbedmicro-rtos-mbed/semaphore/main.cpp
Normal 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
|
||||
544
rtos/tests/TESTS/mbedmicro-rtos-mbed/signals/main.cpp
Normal file
544
rtos/tests/TESTS/mbedmicro-rtos-mbed/signals/main.cpp
Normal 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
|
||||
376
rtos/tests/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp
Normal file
376
rtos/tests/TESTS/mbedmicro-rtos-mbed/systimer/main.cpp
Normal 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);
|
||||
}
|
||||
@@ -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 */
|
||||
863
rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/main.cpp
Normal file
863
rtos/tests/TESTS/mbedmicro-rtos-mbed/threads/main.cpp
Normal 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)
|
||||
Reference in New Issue
Block a user