Rtos API example

Revision:
0:9fca2b23d0ba
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os/TESTS/mbedmicro-rtos-mbed/mail/main.cpp	Sat Feb 23 12:13:36 2019 +0000
@@ -0,0 +1,525 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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 "rtos.h"
+
+#if defined(MBED_RTOS_SINGLE_THREAD)
+  #error [NOT_SUPPORTED] test not supported
+#endif
+
+using namespace utest::v1;
+
+#define THREAD_STACK_SIZE   320 /* larger stack cause out of heap memory on some 16kB RAM boards in multi thread test*/
+#define QUEUE_SIZE          16
+#define THREAD_1_ID         1
+#define THREAD_2_ID         2
+#define THREAD_3_ID         3
+#define QUEUE_PUT_DELAY_1   5
+#define QUEUE_PUT_DELAY_2   50
+#define QUEUE_PUT_DELAY_3   100
+#define DATA_BASE           100
+
+
+typedef struct {
+    uint16_t data;
+    uint8_t thread_id;
+} mail_t;
+
+
+template<uint8_t thread_id, uint32_t wait_ms, uint32_t send_count>
+void send_thread(Mail<mail_t, QUEUE_SIZE> *m)
+{
+    uint32_t data = thread_id * DATA_BASE;
+
+    for (uint32_t i = 0; i < send_count; i++) {
+        mail_t *mail = m->alloc();
+        mail->thread_id = thread_id;
+        mail->data = data++;
+        m->put(mail);
+        Thread::wait(wait_ms);
+    }
+}
+
+template<uint8_t thread_id, uint32_t queue_size, uint32_t wait_ms>
+void receive_thread(Mail<mail_t, queue_size> *m)
+{
+    int result_counter = 0;
+    uint32_t data = thread_id * DATA_BASE;
+
+    Thread::wait(wait_ms);
+    for (uint32_t i = 0; i < queue_size; i++) {
+        osEvent evt = m->get();
+        if (evt.status == osEventMail) {
+            mail_t *mail = (mail_t*)evt.value.p;
+            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(callback(send_thread<THREAD_1_ID, QUEUE_PUT_DELAY_1, QUEUE_SIZE>, &mail_box));
+
+    // wait for some mail to be collected
+    Thread::wait(10);
+
+    for (uint32_t i = 0; i < QUEUE_SIZE; i++) {
+        // mail receive (main thread)
+        osEvent evt = mail_box.get();
+        if (evt.status == osEventMail) {
+            mail_t *mail = (mail_t*)evt.value.p;
+            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(callback(send_thread<THREAD_1_ID, QUEUE_PUT_DELAY_1, 7>, &mail_box));
+    thread2.start(callback(send_thread<THREAD_2_ID, QUEUE_PUT_DELAY_2, 5>, &mail_box));
+    thread3.start(callback(send_thread<THREAD_3_ID, QUEUE_PUT_DELAY_3, 4>, &mail_box));
+
+    // wait for some mail to be collected
+    Thread::wait(10);
+
+    for (uint32_t i = 0; i < QUEUE_SIZE; i++) {
+        // mail receive (main thread)
+        osEvent evt = mail_box.get();
+        if (evt.status == osEventMail) {
+            mail_t *mail = (mail_t*)evt.value.p;
+            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(callback(receive_thread<THREAD_1_ID, 4, 0>, mail_box + 1));
+    thread2.start(callback(receive_thread<THREAD_2_ID, 4, 10>, mail_box + 2));
+    thread3.start(callback(receive_thread<THREAD_3_ID, 4, 100>, mail_box + 3));
+
+    for (uint32_t i = 0; i < 4; i++) {
+        id = THREAD_1_ID;
+        mail = mail_box[id].alloc();
+        mail->thread_id = id;
+        mail->data = data[id]++;
+        mail_box[id].put(mail);
+
+        id = THREAD_2_ID;
+        mail = mail_box[id].alloc();
+        mail->thread_id = id;
+        mail->data = data[id]++;
+        mail_box[id].put(mail);
+
+        id = THREAD_3_ID;
+        mail = mail_box[id].alloc();
+        mail->thread_id = id;
+        mail->data = data[id]++;
+        mail_box[id].put(mail);
+
+        Thread::wait(i * 10);
+    }
+
+    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.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.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail);
+
+    mail = NULL;
+    status = mail_box.free(mail);
+    TEST_ASSERT_EQUAL(osErrorParameter, status);
+}
+
+/** Test same message memory deallocation twice
+
+    Given an empty mailbox
+    Then allocate message memory
+    When try to free it second time
+    Then it return appropriate error code
+ */
+void test_free_twice()
+{
+    osStatus status;
+    Mail<uint32_t, 4> mail_box;
+
+    uint32_t *mail = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail);
+
+    status = mail_box.free(mail);
+    TEST_ASSERT_EQUAL(osOK, status);
+
+    status = mail_box.free(mail);
+    TEST_ASSERT_EQUAL(osErrorResource, status);
+}
+
+/** Test get from empty mailbox with timeout set
+
+    Given an empty mailbox
+    When @a get is called on the mailbox with timeout of 50
+    Then mailbox returns status of osOK, but no data after specified amount of time
+ */
+void test_get_empty_timeout()
+{
+    Mail<uint32_t, 4> mail_box;
+    Timer timer;
+
+    timer.start();
+    osEvent evt = mail_box.get(50);
+    TEST_ASSERT_UINT32_WITHIN(5000, 50000, timer.read_us());
+    TEST_ASSERT_EQUAL(osEventTimeout, evt.status);
+}
+
+/** Test get from empty mailbox with 0 timeout
+
+    Given an empty mailbox
+    When @a get is called on the mailbox with timeout of 0
+    Then mailbox returns status of osOK, but no data
+ */
+void test_get_empty_no_timeout()
+{
+    Mail<uint32_t, 4> mail_box;
+
+    osEvent evt = mail_box.get(0);
+    TEST_ASSERT_EQUAL(osOK, evt.status);
+}
+
+/** 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;
+    osEvent evt;
+    Mail<int32_t, 4> mail_box;
+    const int32_t TEST_VAL1 = 123;
+    const int32_t TEST_VAL2 = 456;
+
+    int32_t *mail1 = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail1);
+
+    *mail1 = TEST_VAL1;
+    status = mail_box.put(mail1);
+    TEST_ASSERT_EQUAL(osOK, status);
+
+    int32_t *mail2 = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail2);
+
+    *mail2 = TEST_VAL2;
+    status = mail_box.put(mail2);
+    TEST_ASSERT_EQUAL(osOK, status);
+
+
+    evt = mail_box.get();
+    TEST_ASSERT_EQUAL(evt.status, osEventMail);
+
+    mail1 = (int32_t*)evt.value.p;
+    TEST_ASSERT_EQUAL(TEST_VAL1, *mail1);
+
+    evt = mail_box.get();
+    TEST_ASSERT_EQUAL(evt.status, osEventMail);
+
+    mail2 = (int32_t*)evt.value.p;
+    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 alloc four times it returns memory blocks
+    Then the memory blocks should be valid
+    When call @a 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;
+    const uint32_t TEST_VAL = 123;
+
+    // 1 OK
+    uint32_t *mail1 = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail1);
+
+    // 2 OK
+    uint32_t *mail2 = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail2);
+
+    // 3 OK
+    uint32_t *mail3 = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail3);
+
+    // 4 OK
+    uint32_t *mail4 = mail_box.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail4);
+
+    // 5 KO
+    uint32_t *mail5 = mail_box.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.alloc();
+    TEST_ASSERT_NOT_EQUAL(NULL, mail);
+
+    *mail = TEST_VAL;
+    status = mail_box.put(mail);
+    TEST_ASSERT_EQUAL(osOK, status);
+
+    osEvent evt = mail_box.get();
+    TEST_ASSERT_EQUAL(evt.status, osEventMail);
+
+    mail = (T*)evt.value.p;
+    TEST_ASSERT_EQUAL(TEST_VAL, *mail);
+
+
+    status = mail_box.free(mail);
+    TEST_ASSERT_EQUAL(osOK, status);
+}
+
+/** Test calloc - memory block allocation with resetting
+
+    Given an empty Mail box
+    When call @a 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.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.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.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 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 message free twice", test_free_twice),
+    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);
+}