|
14 | 14 | * limitations under the License.
|
15 | 15 | */
|
16 | 16 | #include "mbed.h"
|
17 |
| -#include "test_env.h" |
18 |
| -#include "rtos.h" |
| 17 | +#include "greentea-client/test_env.h" |
| 18 | +#include "utest/utest.h" |
| 19 | +#include "unity/unity.h" |
| 20 | + |
19 | 21 |
|
20 | 22 | #if defined(MBED_RTOS_SINGLE_THREAD)
|
21 | 23 | #error [NOT_SUPPORTED] test not supported
|
22 | 24 | #endif
|
23 | 25 |
|
24 |
| -#define NUM_THREADS 5 |
| 26 | +using utest::v1::Case; |
| 27 | + |
| 28 | +extern uint32_t mbed_heap_size; |
| 29 | +static const int test_timeout = 25; |
| 30 | +volatile bool thread_should_continue = true; |
| 31 | +#define NUM_THREADS 4 |
| 32 | +#define THREAD_MALLOC_SIZE 100 |
25 | 33 |
|
26 | 34 | #if defined(__CORTEX_A9)
|
27 | 35 | #define THREAD_STACK_SIZE DEFAULT_STACK_SIZE
|
28 | 36 | #else
|
29 | 37 | #define THREAD_STACK_SIZE 256
|
30 | 38 | #endif
|
31 | 39 |
|
32 |
| -DigitalOut led1(LED1); |
33 |
| -volatile bool should_exit = false; |
34 |
| -volatile bool allocation_failure = false; |
35 | 40 |
|
36 | 41 | void task_using_malloc(void)
|
37 | 42 | {
|
38 |
| - void* data; |
39 |
| - while (1) { |
| 43 | + void *data = NULL; |
| 44 | + |
| 45 | + while (thread_should_continue) { |
40 | 46 | // Repeatedly allocate and free memory
|
41 |
| - data = malloc(100); |
42 |
| - if (data != NULL) { |
43 |
| - memset(data, 0, 100); |
44 |
| - } else { |
45 |
| - allocation_failure = true; |
46 |
| - } |
47 |
| - free(data); |
| 47 | + data = malloc(THREAD_MALLOC_SIZE); |
| 48 | + TEST_ASSERT_NOT_NULL(data); |
48 | 49 |
|
49 |
| - if (should_exit) { |
50 |
| - return; |
51 |
| - } |
| 50 | + // test whole allocated memory |
| 51 | + memset(data, 0, THREAD_MALLOC_SIZE); |
| 52 | + |
| 53 | + free(data); |
52 | 54 | }
|
53 | 55 | }
|
54 | 56 |
|
55 |
| -int main() |
| 57 | +/** Test for multithreaded heap allocations |
| 58 | +
|
| 59 | + Given multiple threads are started in parallel |
| 60 | + When each of the threads allocate memory |
| 61 | + Then the memory allocation succeed and @a malloc return valid memory |
| 62 | + */ |
| 63 | +void test_multithread_allocation(void) |
56 | 64 | {
|
57 | 65 | // static stack for threads to reduce heap usage on devices with small RAM
|
58 | 66 | // and eliminate run out of heap memory problem
|
59 | 67 | uint8_t stack[NUM_THREADS][THREAD_STACK_SIZE];
|
60 | 68 |
|
| 69 | + bool thread_alloc_failure = false; |
61 | 70 | Thread *thread_list[NUM_THREADS];
|
62 |
| - int test_time = 15; |
63 |
| - GREENTEA_SETUP(20, "default_auto"); |
| 71 | + int test_time = 20; |
64 | 72 |
|
65 | 73 | // Allocate threads for the test
|
66 | 74 | for (int i = 0; i < NUM_THREADS; i++) {
|
67 | 75 | thread_list[i] = new Thread(osPriorityNormal, THREAD_STACK_SIZE, stack[i]);
|
68 | 76 | if (NULL == thread_list[i]) {
|
69 |
| - allocation_failure = true; |
| 77 | + thread_alloc_failure = true; |
70 | 78 | } else {
|
71 | 79 | thread_list[i]->start(task_using_malloc);
|
72 | 80 | }
|
73 | 81 | }
|
74 | 82 |
|
75 | 83 | // Give the test time to run
|
76 |
| - while (test_time) { |
77 |
| - led1 = !led1; |
| 84 | + while (test_time--) { |
78 | 85 | Thread::wait(1000);
|
79 |
| - test_time--; |
80 | 86 | }
|
81 | 87 |
|
82 | 88 | // Join and delete all threads
|
83 |
| - should_exit = 1; |
| 89 | + thread_should_continue = false; |
84 | 90 | for (int i = 0; i < NUM_THREADS; i++) {
|
85 |
| - if (NULL == thread_list[i]) { |
86 |
| - continue; |
| 91 | + if (NULL != thread_list[i]) { |
| 92 | + thread_list[i]->join(); |
| 93 | + delete thread_list[i]; |
| 94 | + thread_list[i] = NULL; |
87 | 95 | }
|
88 |
| - thread_list[i]->join(); |
89 |
| - delete thread_list[i]; |
90 | 96 | }
|
| 97 | + TEST_ASSERT_FALSE(thread_alloc_failure); |
| 98 | +} |
| 99 | + |
| 100 | +/** Test for large heap allocation |
| 101 | +
|
| 102 | + Given a heap of size mbed_heap_size |
| 103 | + When try to allocate memory of size mbed_heap_size/5 (20% of whole heap) |
| 104 | + Then the memory is allocated and @a malloc return valid memory |
| 105 | + */ |
| 106 | +void test_big_allocation(void) |
| 107 | +{ |
| 108 | + const uint32_t alloc_size = mbed_heap_size / 5; |
| 109 | + void *data = NULL; |
| 110 | + |
| 111 | + data = malloc(alloc_size); |
| 112 | + TEST_ASSERT_NOT_NULL(data); |
| 113 | + |
| 114 | + // test whole allocated memory |
| 115 | + memset(data, 0, alloc_size); |
| 116 | + |
| 117 | + free(data); |
| 118 | +} |
| 119 | + |
| 120 | +/** Test if allocation of zero size does not cause any undefined behaviour |
| 121 | +
|
| 122 | + Given a heap |
| 123 | + When try to allocate memory of size 0 |
| 124 | + Then the return value of @a malloc depends on the particular library implementation |
| 125 | + (NULL or smallest possible allocation) and no undefined behaviour happens |
| 126 | +
|
| 127 | + @note If allocation size is zero, the return value depends on the particular library implementation |
| 128 | + (it may or may not be a null pointer), but the returned pointer shall not be dereferenced |
| 129 | + */ |
| 130 | +void test_zero_allocation(void) |
| 131 | +{ |
| 132 | + void *data = NULL; |
| 133 | + |
| 134 | + data = malloc(0); |
| 135 | + if(data != NULL) { |
| 136 | + free(data); |
| 137 | + } |
| 138 | + TEST_ASSERT_MESSAGE(true, "malloc(0) succeed - no undefined behaviour happens"); |
| 139 | +} |
| 140 | + |
| 141 | +/** Test if free on NULL pointer does not cause any undefined behaviour |
| 142 | +
|
| 143 | + Given a NULL pointer |
| 144 | + When try to free it |
| 145 | + Then the function @a free does nothing and no undefined behaviour happens |
| 146 | + */ |
| 147 | +void test_null_free(void) |
| 148 | +{ |
| 149 | + void *data = NULL; |
| 150 | + free(data); |
| 151 | + |
| 152 | + TEST_ASSERT_MESSAGE(true, "free(NULL) succeed - no undefined behaviour happens"); |
| 153 | +} |
| 154 | + |
| 155 | +// Test cases |
| 156 | +Case cases[] = { |
| 157 | + Case("Test 0 size allocation", test_zero_allocation), |
| 158 | + Case("Test NULL pointer free", test_null_free), |
| 159 | + Case("Test multithreaded allocations", test_multithread_allocation), |
| 160 | + Case("Test large allocation", test_big_allocation) |
| 161 | +}; |
91 | 162 |
|
92 |
| - GREENTEA_TESTSUITE_RESULT(!allocation_failure); |
| 163 | +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) |
| 164 | +{ |
| 165 | + GREENTEA_SETUP(test_timeout, "timing_drift_auto"); |
| 166 | + return utest::v1::greentea_test_setup_handler(number_of_cases); |
| 167 | +} |
| 168 | + |
| 169 | +utest::v1::Specification specification(greentea_test_setup, cases); |
| 170 | + |
| 171 | +int main() |
| 172 | +{ |
| 173 | + return !utest::v1::Harness::run(specification); |
93 | 174 | }
|
0 commit comments