Skip to content

RTOS: Queue tests & docs rework #4752

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
339 changes: 280 additions & 59 deletions TESTS/mbedmicro-rtos-mbed/queue/main.cpp
Original file line number Diff line number Diff line change
@@ -1,70 +1,291 @@
#include "mbed.h"
#include "greentea-client/test_env.h"
#include "unity.h"
#include "utest.h"
#include "rtos.h"

#if defined(MBED_RTOS_SINGLE_THREAD)
#error [NOT_SUPPORTED] test not supported
#endif

typedef struct {
float voltage; /* AD result of measured voltage */
float current; /* AD result of measured current */
uint32_t counter; /* A counter value */
} message_t;

#define CREATE_VOLTAGE(COUNTER) (COUNTER * 0.1) * 33
#define CREATE_CURRENT(COUNTER) (COUNTER * 0.1) * 11
#define QUEUE_SIZE 16
#define QUEUE_PUT_DELAY 100

#define TEST_STACK_SIZE 512

MemoryPool<message_t, QUEUE_SIZE> mpool;
Queue<message_t, QUEUE_SIZE> queue;

/* Send Thread */
void send_thread () {
static uint32_t i = 10;
while (true) {
i++; // Fake data update
message_t *message = mpool.alloc();
message->voltage = CREATE_VOLTAGE(i);
message->current = CREATE_CURRENT(i);
message->counter = i;
queue.put(message);
Thread::wait(QUEUE_PUT_DELAY);
}
using namespace utest::v1;

#define THREAD_STACK_SIZE 512
#define TEST_UINT_MSG 0xDEADBEEF
#define TEST_UINT_MSG2 0xE1EE7
#define TEST_TIMEOUT 50

template <uint32_t ms>
void thread_put_uint_msg(Queue<uint32_t, 1> *q)
{
Thread::wait(ms);
osStatus stat = q->put((uint32_t*) TEST_UINT_MSG);
TEST_ASSERT_EQUAL(osOK, stat);
}

template <uint32_t ms, uint32_t val>
void thread_get_uint_msg(Queue<uint32_t, 1> *q)
{
Thread::wait(ms);
osEvent evt = q->get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(val, evt.value.v);
}

/** Test pass uint 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_uint()
{
Queue<uint32_t, 1> q;
osStatus stat = q.put((uint32_t*)TEST_UINT_MSG);
TEST_ASSERT_EQUAL(osOK, stat);

osEvent evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG, evt.value.v);
}

/** Test pass uint 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_uint_twice()
{
Queue<uint32_t, 1> q;
osStatus stat = q.put((uint32_t*)TEST_UINT_MSG);
TEST_ASSERT_EQUAL(osOK, stat);

osEvent evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG, evt.value.v);

stat = q.put((uint32_t*)TEST_UINT_MSG2);
TEST_ASSERT_EQUAL(osOK, stat);

evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG2, evt.value.v);
}

/** Test pass ptr msg

Given a queue for pointers to uint32_t messages with one slot
When a pointer to an uint32_t 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_ptr()
{
Queue<uint32_t, 1> q;
uint32_t msg = TEST_UINT_MSG;

osStatus stat = q.put(&msg);
TEST_ASSERT_EQUAL(osOK, stat);

osEvent evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(&msg, evt.value.p);
}

/** 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 osOK, but no data
*/
void test_get_empty_no_timeout()
{
Queue<uint32_t, 1> q;

osEvent evt = q.get(0);
TEST_ASSERT_EQUAL(osOK, evt.status);
}

/** 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;
uint32_t start = us_ticker_read();

osEvent evt = q.get(50);
TEST_ASSERT_EQUAL(osEventTimeout, evt.status);
TEST_ASSERT_UINT32_WITHIN(5000, 50000, us_ticker_read() - start);
}

/** 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<TEST_TIMEOUT>, &q));

uint32_t start = us_ticker_read();

osEvent evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG, evt.value.v);
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, us_ticker_read() - start);
}

int main (void) {
GREENTEA_SETUP(20, "default_auto");

Thread thread(osPriorityNormal, TEST_STACK_SIZE);
thread.start(send_thread);
bool result = true;
int result_counter = 0;

while (true) {
osEvent evt = queue.get();
if (evt.status == osEventMessage) {
message_t *message = (message_t*)evt.value.p;
const float expected_voltage = CREATE_VOLTAGE(message->counter);
const float expected_current = CREATE_CURRENT(message->counter);
// Check using macros if received values correspond to values sent via queue
bool expected_values = (expected_voltage == message->voltage) &&
(expected_current == message->current);
result = result && expected_values;
const char *result_msg = expected_values ? "OK" : "FAIL";
printf("%3d %.2fV %.2fA ... [%s]\r\n", message->counter,
message->voltage,
message->current,
result_msg);
mpool.free(message);
if (result == false || ++result_counter == QUEUE_SIZE) {
break;
}
}
}
GREENTEA_TESTSUITE_RESULT(result);
return 0;
/** 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 with @a osErrorResource
*/
void test_put_full_no_timeout()
{
Queue<uint32_t, 1> q;

osStatus stat = q.put((uint32_t*) TEST_UINT_MSG);
TEST_ASSERT_EQUAL(osOK, stat);

stat = q.put((uint32_t*) TEST_UINT_MSG);
TEST_ASSERT_EQUAL(osErrorResource, 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 with @a osErrorTimeout
*/
void test_put_full_timeout()
{
Queue<uint32_t, 1> q;

osStatus stat = q.put((uint32_t*) TEST_UINT_MSG, TEST_TIMEOUT);
TEST_ASSERT_EQUAL(osOK, stat);

uint32_t start = us_ticker_read();

stat = q.put((uint32_t*) TEST_UINT_MSG, TEST_TIMEOUT);
TEST_ASSERT_EQUAL(osErrorTimeout, stat);
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, us_ticker_read() - start);
}

/** 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 osWaitForever 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<TEST_TIMEOUT, TEST_UINT_MSG>, &q));

osStatus stat = q.put((uint32_t*) TEST_UINT_MSG);
TEST_ASSERT_EQUAL(osOK, stat);

uint32_t start = us_ticker_read();
stat = q.put((uint32_t*) TEST_UINT_MSG, osWaitForever);
TEST_ASSERT_EQUAL(osOK, stat);
TEST_ASSERT_UINT32_WITHIN(TEST_TIMEOUT * 100, TEST_TIMEOUT * 1000, us_ticker_read() - start);

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;

osStatus stat = q.put((uint32_t*) TEST_UINT_MSG, TEST_TIMEOUT);
TEST_ASSERT_EQUAL(osOK, stat);

stat = q.put((uint32_t*) TEST_UINT_MSG2, TEST_TIMEOUT);
TEST_ASSERT_EQUAL(osOK, stat);

osEvent evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG, evt.value.v);

evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG2, evt.value.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;

osStatus stat = q.put((uint32_t*) TEST_UINT_MSG, TEST_TIMEOUT, 0);
TEST_ASSERT_EQUAL(osOK, stat);

stat = q.put((uint32_t*) TEST_UINT_MSG2, TEST_TIMEOUT, 1);
TEST_ASSERT_EQUAL(osOK, stat);

osEvent evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG2, evt.value.v);

evt = q.get();
TEST_ASSERT_EQUAL(osEventMessage, evt.status);
TEST_ASSERT_EQUAL(TEST_UINT_MSG, evt.value.v);
}

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 uint msg", test_pass_uint),
Case("Test pass uint msg twice", test_pass_uint_twice),
Case("Test pass ptr msg", test_pass_ptr),
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)
};

Specification specification(test_setup, cases);

int main()
{
return !Harness::run(specification);
}
15 changes: 12 additions & 3 deletions rtos/Queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,24 @@ class Queue : private mbed::NonCopyable<Queue<T, queue_sz> > {
@param data message pointer.
@param millisec timeout value or 0 in case of no time-out. (default: 0)
@param prio priority value or 0 in case of default. (default: 0)
@return status code that indicates the execution status of the function.
@return status code that indicates the execution status of the function:
@a osOK the message has been put into the queue.
@a osErrorTimeout the message could not be put into the queue in the given time.
@a osErrorResource not enough space in the queue.
@a osErrorParameter internal error or non-zero timeout specified in an ISR.
*/
osStatus put(T* data, uint32_t millisec=0, uint8_t prio=0) {
return osMessageQueuePut(_id, &data, prio, millisec);
}

/** Get a message or Wait for a message from a Queue.
/** Get a message or Wait for a message from a Queue. Messages are retrieved in a descending priority order or
first in first out when the priorities are the same.
@param millisec timeout value or 0 in case of no time-out. (default: osWaitForever).
@return event information that includes the message and the status code.
@return event information that includes the message in event.value and the status code in event.status:
@a osEventMessage message received.
@a osOK no message is available in the queue and no timeout was specified.
@a osEventTimeout no message has arrived during the given timeout period.
@a osErrorParameter a parameter is invalid or outside of a permitted range.
*/
osEvent get(uint32_t millisec=osWaitForever) {
osEvent event;
Expand Down