|
15 | 15 | */
|
16 | 16 |
|
17 | 17 | #if !DEVICE_LOWPOWERTIMER
|
18 |
| - #error [NOT_SUPPORTED] Low power timer not supported for this target |
| 18 | +#error [NOT_SUPPORTED] Low power timer not supported for this target |
19 | 19 | #endif
|
20 | 20 |
|
21 | 21 | #include "utest/utest.h"
|
22 | 22 | #include "unity/unity.h"
|
23 | 23 | #include "greentea-client/test_env.h"
|
24 | 24 |
|
25 | 25 | #include "mbed.h"
|
| 26 | +#include "rtos.h" |
26 | 27 |
|
27 | 28 | using namespace utest::v1;
|
28 | 29 |
|
| 30 | +#define NUM_TIMEOUTS 64 |
| 31 | +const float TEST_DELAY_S = 0.01; |
| 32 | +const uint32_t TEST_DELAY_MS = 1000.0F * TEST_DELAY_S; |
| 33 | +const us_timestamp_t TEST_DELAY_US = 1000000.0F * TEST_DELAY_S; |
| 34 | + |
| 35 | +void sem_callback(Semaphore *sem) |
| 36 | +{ |
| 37 | + sem->release(); |
| 38 | +} |
| 39 | + |
| 40 | +void cnt_callback(volatile uint32_t *cnt) |
| 41 | +{ |
| 42 | + (*cnt)++; |
| 43 | +} |
| 44 | + |
| 45 | +class TimeoutAttachUSTester: public LowPowerTimeout { |
| 46 | +public: |
| 47 | + void attach_callback(Callback<void()> func, us_timestamp_t delay_us) |
| 48 | + { |
| 49 | + attach_us(func, delay_us); |
| 50 | + } |
| 51 | +}; |
| 52 | + |
| 53 | +class TimeoutAttachTester: public LowPowerTimeout { |
| 54 | +public: |
| 55 | + void attach_callback(Callback<void()> func, us_timestamp_t delay_us) |
| 56 | + { |
| 57 | + attach(func, (float) delay_us / 1000000.0f); |
| 58 | + } |
| 59 | +}; |
| 60 | + |
| 61 | +/** Template for tests: callback called once |
| 62 | + * |
| 63 | + * Test callback called once |
| 64 | + * Given a LowPowerTimeout object with a callback attached with @a attach() |
| 65 | + * When given time elapses |
| 66 | + * Then the callback is called exactly one time |
| 67 | + * |
| 68 | + * Test callback called once |
| 69 | + * Given a LowPowerTimeout object with a callback attached with @a attach_us() |
| 70 | + * When given time elapses |
| 71 | + * Then the callback is called exactly one time |
| 72 | + */ |
| 73 | +template<typename T> |
| 74 | +void test_callback_fires_once(void) |
| 75 | +{ |
| 76 | + Semaphore sem(0, 1); |
| 77 | + T timeout; |
| 78 | + |
| 79 | + timeout.attach_callback(mbed::callback(sem_callback, &sem), TEST_DELAY_US); |
| 80 | + |
| 81 | + int32_t sem_slots = sem.wait(0); |
| 82 | + TEST_ASSERT_EQUAL(0, sem_slots); |
| 83 | + |
| 84 | + sem_slots = sem.wait(TEST_DELAY_MS + 1); |
| 85 | + TEST_ASSERT_EQUAL(1, sem_slots); |
| 86 | + |
| 87 | + sem_slots = sem.wait(TEST_DELAY_MS + 1); |
| 88 | + TEST_ASSERT_EQUAL(0, sem_slots); |
| 89 | + |
| 90 | + timeout.detach(); |
| 91 | +} |
| 92 | + |
| 93 | +/** Template for tests: callback not called when cancelled |
| 94 | + * |
| 95 | + * Test callback not called when cancelled |
| 96 | + * Given a LowPowerTimeout object with a callback attached with @a attach() |
| 97 | + * When the callback is detached before being called |
| 98 | + * Then the callback is never called |
| 99 | + * |
| 100 | + * Test callback not called when cancelled |
| 101 | + * Given a LowPowerTimeout object with a callback attached with @a attach_us() |
| 102 | + * When the callback is detached before being called |
| 103 | + * Then the callback is never called |
| 104 | + */ |
| 105 | +template<typename T> |
| 106 | +void test_cancel(void) |
| 107 | +{ |
| 108 | + Semaphore sem(0, 1); |
| 109 | + T timeout; |
| 110 | + |
| 111 | + timeout.attach_callback(mbed::callback(sem_callback, &sem), 2.0f * TEST_DELAY_US); |
| 112 | + |
| 113 | + int32_t sem_slots = sem.wait(TEST_DELAY_MS); |
| 114 | + TEST_ASSERT_EQUAL(0, sem_slots); |
| 115 | + timeout.detach(); |
| 116 | + |
| 117 | + sem_slots = sem.wait(TEST_DELAY_MS + 1); |
| 118 | + TEST_ASSERT_EQUAL(0, sem_slots); |
| 119 | +} |
| 120 | + |
| 121 | +/** Template for tests: callback override |
| 122 | + * |
| 123 | + * Test callback override |
| 124 | + * Given a LowPowerTimeout object with a callback attached with @a attach() |
| 125 | + * When another callback is attached before first one is called |
| 126 | + * and second callback's delay elapses |
| 127 | + * Then the second callback is called |
| 128 | + * and the first callback is never called |
| 129 | + * |
| 130 | + * Test callback override |
| 131 | + * Given a LowPowerTimeout object with a callback attached with @a attach_us() |
| 132 | + * When another callback is attached before first one is called |
| 133 | + * and second callback's delay elapses |
| 134 | + * Then the second callback is called |
| 135 | + * and the first callback is never called |
| 136 | + */ |
| 137 | +template<typename T> |
| 138 | +void test_override(void) |
| 139 | +{ |
| 140 | + Semaphore sem1(0, 1); |
| 141 | + Semaphore sem2(0, 1); |
| 142 | + T timeout; |
| 143 | + |
| 144 | + timeout.attach_callback(mbed::callback(sem_callback, &sem1), 2.0f * TEST_DELAY_US); |
| 145 | + |
| 146 | + int32_t sem_slots = sem1.wait(TEST_DELAY_MS); |
| 147 | + TEST_ASSERT_EQUAL(0, sem_slots); |
| 148 | + timeout.attach_callback(mbed::callback(sem_callback, &sem2), 2.0f * TEST_DELAY_US); |
| 149 | + |
| 150 | + sem_slots = sem2.wait(2 * TEST_DELAY_MS + 1); |
| 151 | + TEST_ASSERT_EQUAL(1, sem_slots); |
| 152 | + sem_slots = sem1.wait(0); |
| 153 | + TEST_ASSERT_EQUAL(0, sem_slots); |
| 154 | + |
| 155 | + timeout.detach(); |
| 156 | +} |
| 157 | + |
| 158 | +/** Template for tests: multiple LowPowerTimeouts |
| 159 | + * |
| 160 | + * Test multiple LowPowerTimeouts |
| 161 | + * Given multiple separate LowPowerTimeout objects |
| 162 | + * When a callback is attached to all of these LowPowerTimeout objects with @a attach() |
| 163 | + * and delay for every LowPowerTimeout elapses |
| 164 | + * Then all callbacks are called |
| 165 | + * |
| 166 | + * Test multiple LowPowerTimeouts |
| 167 | + * Given multiple separate LowPowerTimeout objects |
| 168 | + * When a callback is attached to all of these LowPowerTimeout objects with @a attach_us() |
| 169 | + * and delay for every LowPowerTimeout elapses |
| 170 | + * Then all callbacks are called |
| 171 | + */ |
| 172 | +template<typename T> |
| 173 | +void test_multiple(void) |
| 174 | +{ |
| 175 | + volatile uint32_t callback_count = 0; |
| 176 | + T timeouts[NUM_TIMEOUTS]; |
| 177 | + for (size_t i = 0; i < NUM_TIMEOUTS; i++) { |
| 178 | + timeouts[i].attach_callback(mbed::callback(cnt_callback, &callback_count), TEST_DELAY_US); |
| 179 | + } |
| 180 | + Thread::wait(TEST_DELAY_MS + 1); |
| 181 | + TEST_ASSERT_EQUAL(NUM_TIMEOUTS, callback_count); |
| 182 | +} |
| 183 | + |
| 184 | +/** Template for tests: zero delay |
| 185 | + * |
| 186 | + * Test zero delay |
| 187 | + * Given a LowPowerTimeout object |
| 188 | + * When a callback is attached with 0.0 s delay, with @a attach() |
| 189 | + * Then the callback is called instantly |
| 190 | + * |
| 191 | + * Test zero delay |
| 192 | + * Given a LowPowerTimeout object |
| 193 | + * When a callback is attached with 0.0 s delay, with @a attach_us() |
| 194 | + * Then the callback is called instantly |
| 195 | + */ |
| 196 | +template<typename T> |
| 197 | +void test_no_wait(void) |
| 198 | +{ |
| 199 | + Semaphore sem(0, 1); |
| 200 | + T timeout; |
| 201 | + timeout.attach_callback(mbed::callback(sem_callback, &sem), 0ULL); |
| 202 | + |
| 203 | + int32_t sem_slots = sem.wait(0); |
| 204 | + TEST_ASSERT_EQUAL(1, sem_slots); |
| 205 | + timeout.detach(); |
| 206 | +} |
| 207 | + |
29 | 208 | volatile static bool complete;
|
30 | 209 | static LowPowerTimeout lpt;
|
31 | 210 |
|
32 | 211 | /* Timeouts are quite arbitrary due to large number of boards with varying level of accuracy */
|
33 | 212 | #define LONG_TIMEOUT (100000)
|
34 | 213 | #define SHORT_TIMEOUT (600)
|
35 | 214 |
|
36 |
| -void cb_done() { |
| 215 | +void cb_done() |
| 216 | +{ |
37 | 217 | complete = true;
|
38 | 218 | }
|
39 | 219 |
|
@@ -119,29 +299,48 @@ void lp_timeout_500us(void)
|
119 | 299 |
|
120 | 300 | }
|
121 | 301 |
|
122 |
| -utest::v1::status_t greentea_failure_handler(const Case *const source, const failure_t reason) { |
| 302 | +utest::v1::status_t greentea_failure_handler(const Case * const source, const failure_t reason) |
| 303 | +{ |
123 | 304 | greentea_case_failure_abort_handler(source, reason);
|
124 | 305 | return STATUS_CONTINUE;
|
125 | 306 | }
|
126 | 307 |
|
127 | 308 | Case cases[] = {
|
| 309 | + Case("Test callback called once (with attach)", test_callback_fires_once<TimeoutAttachTester>), |
| 310 | + Case("Test callback called once (with attach_us)", test_callback_fires_once<TimeoutAttachUSTester>), |
| 311 | + |
| 312 | + Case("Test callback not called when cancelled (with attach)", test_cancel<TimeoutAttachTester>), |
| 313 | + Case("Test callback not called when cancelled (with attach_us)", test_cancel<TimeoutAttachUSTester>), |
| 314 | + |
| 315 | + Case("Test callback override (with attach)", test_override<TimeoutAttachTester>), |
| 316 | + Case("Test callback override (with attach_us)", test_override<TimeoutAttachUSTester>), |
| 317 | + |
| 318 | + Case("Test multiple timeouts running in parallel (with attach)", test_multiple<TimeoutAttachTester>), |
| 319 | + Case("Test multiple timeouts running in parallel (with attach_us)", test_multiple<TimeoutAttachUSTester>), |
| 320 | + |
| 321 | + Case("Test zero delay (with attach)", test_no_wait<TimeoutAttachTester>), |
| 322 | + Case("Test zero delay (with attach_us)", test_no_wait<TimeoutAttachUSTester>), |
| 323 | + |
128 | 324 | Case("500us LowPowerTimeout", lp_timeout_500us, greentea_failure_handler),
|
129 | 325 | Case("1ms LowPowerTimeout", lp_timeout_1ms, greentea_failure_handler),
|
130 | 326 | Case("1sec LowPowerTimeout", lp_timeout_1s, greentea_failure_handler),
|
131 | 327 | Case("5sec LowPowerTimeout", lp_timeout_5s, greentea_failure_handler),
|
| 328 | + |
132 | 329 | #if DEVICE_SLEEP
|
133 | 330 | Case("1sec LowPowerTimeout from sleep", lp_timeout_1s_sleep, greentea_failure_handler),
|
134 | 331 | Case("1sec LowPowerTimeout from deepsleep", lp_timeout_1s_deepsleep, greentea_failure_handler),
|
135 | 332 | #endif /* DEVICE_SLEEP */
|
136 | 333 | };
|
137 | 334 |
|
138 |
| -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) { |
| 335 | +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) |
| 336 | +{ |
139 | 337 | GREENTEA_SETUP(20, "default_auto");
|
140 | 338 | return greentea_test_setup_handler(number_of_cases);
|
141 | 339 | }
|
142 | 340 |
|
143 | 341 | Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
144 | 342 |
|
145 |
| -int main() { |
| 343 | +int main() |
| 344 | +{ |
146 | 345 | Harness::run(specification);
|
147 | 346 | }
|
0 commit comments