Marco Zecchini
/
Example_RTOS
Rtos API example
Diff: mbed-os/TESTS/mbedmicro-rtos-mbed/mail/main.cpp
- 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); +}