1 /*
2 * Copyright (c) 2023, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "test_platform.h"
30
31 #include "common/code_utils.hpp"
32 #include "common/debug.hpp"
33 #include "common/num_utils.hpp"
34 #include "common/trickle_timer.hpp"
35 #include "instance/instance.hpp"
36
37 namespace ot {
38
39 static Instance *sInstance;
40
41 static uint32_t sNow = 0;
42 static uint32_t sAlarmTime;
43 static bool sAlarmOn = false;
44
45 extern "C" {
46
otPlatAlarmMilliStop(otInstance *)47 void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; }
48
otPlatAlarmMilliStartAt(otInstance *,uint32_t aT0,uint32_t aDt)49 void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
50 {
51 sAlarmOn = true;
52 sAlarmTime = aT0 + aDt;
53 }
54
otPlatAlarmMilliGetNow(void)55 uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
56
57 } // extern "C"
58
AdvanceTime(uint32_t aDuration)59 void AdvanceTime(uint32_t aDuration)
60 {
61 uint32_t time = sNow + aDuration;
62
63 while (TimeMilli(sAlarmTime) <= TimeMilli(time))
64 {
65 sNow = sAlarmTime;
66 otPlatAlarmMilliFired(sInstance);
67 }
68
69 sNow = time;
70 }
71
72 class TrickleTimerTester : public TrickleTimer
73 {
74 public:
TrickleTimerTester(Instance & aInstance)75 explicit TrickleTimerTester(Instance &aInstance)
76 : TrickleTimer(aInstance, HandleTimerFired)
77 , mDidFire(false)
78 {
79 }
80
GetFireTime(void) const81 Time GetFireTime(void) const { return TimerMilli::GetFireTime(); }
GetInterval(void) const82 uint32_t GetInterval(void) const { return TrickleTimer::mInterval; }
GetTimeInInterval(void) const83 uint32_t GetTimeInInterval(void) const { return TrickleTimer::mTimeInInterval; }
84
VerifyTimerDidFire(void)85 void VerifyTimerDidFire(void)
86 {
87 VerifyOrQuit(mDidFire);
88 mDidFire = false;
89 }
90
VerifyTimerDidNotFire(void) const91 void VerifyTimerDidNotFire(void) const { VerifyOrQuit(!mDidFire); }
92
RemoveAll(Instance & aInstance)93 static void RemoveAll(Instance &aInstance) { TimerMilli::RemoveAll(aInstance); }
94
95 private:
HandleTimerFired(TrickleTimer & aTimer)96 static void HandleTimerFired(TrickleTimer &aTimer) { static_cast<TrickleTimerTester &>(aTimer).HandleTimerFired(); }
HandleTimerFired(void)97 void HandleTimerFired(void) { mDidFire = true; }
98
99 bool mDidFire;
100 };
101
AlarmFired(otInstance * aInstance)102 void AlarmFired(otInstance *aInstance) { otPlatAlarmMilliFired(aInstance); }
103
TestTrickleTimerPlainMode(void)104 void TestTrickleTimerPlainMode(void)
105 {
106 static constexpr uint32_t kMinInterval = 2000;
107 static constexpr uint32_t kMaxInterval = 5000;
108
109 Instance *instance = testInitInstance();
110 TrickleTimerTester timer(*instance);
111 uint32_t interval;
112
113 sInstance = instance;
114 TrickleTimerTester::RemoveAll(*instance);
115
116 printf("TestTrickleTimerPlainMode() ");
117
118 // Validate that timer picks a random interval between min and max
119 // on start.
120
121 sNow = 1000;
122 timer.Start(TrickleTimer::kModePlainTimer, kMinInterval, kMaxInterval, 0);
123
124 VerifyOrQuit(timer.IsRunning());
125 VerifyOrQuit(timer.GetIntervalMax() == kMaxInterval);
126 VerifyOrQuit(timer.GetIntervalMin() == kMinInterval);
127
128 interval = timer.GetInterval();
129 VerifyOrQuit((interval >= kMinInterval) && (interval <= kMaxInterval));
130
131 for (uint8_t iter = 0; iter <= 10; iter++)
132 {
133 AdvanceTime(interval);
134
135 timer.VerifyTimerDidFire();
136
137 // The plain mode trickle timer restarts with a new random
138 // interval between min and max.
139
140 VerifyOrQuit(timer.IsRunning());
141 interval = timer.GetInterval();
142 VerifyOrQuit((interval >= kMinInterval) && (interval <= kMaxInterval));
143 }
144
145 printf(" --> PASSED\n");
146
147 testFreeInstance(instance);
148 }
149
TestTrickleTimerTrickleMode(uint32_t aRedundancyConstant,uint32_t aConsistentCalls)150 void TestTrickleTimerTrickleMode(uint32_t aRedundancyConstant, uint32_t aConsistentCalls)
151 {
152 static constexpr uint32_t kMinInterval = 1000;
153 static constexpr uint32_t kMaxInterval = 9000;
154
155 Instance *instance = testInitInstance();
156 TrickleTimerTester timer(*instance);
157 uint32_t interval;
158 uint32_t t;
159
160 sInstance = instance;
161 TrickleTimerTester::RemoveAll(*instance);
162
163 printf("TestTrickleTimerTrickleMode(aRedundancyConstant:%u, aConsistentCalls:%u) ", aRedundancyConstant,
164 aConsistentCalls);
165
166 sNow = 1000;
167 timer.Start(TrickleTimer::kModeTrickle, kMinInterval, kMaxInterval, aRedundancyConstant);
168
169 // Validate that trickle timer starts with random interval between
170 // min/max.
171
172 VerifyOrQuit(timer.IsRunning());
173 VerifyOrQuit(timer.GetIntervalMax() == kMaxInterval);
174 VerifyOrQuit(timer.GetIntervalMin() == kMinInterval);
175
176 interval = timer.GetInterval();
177 VerifyOrQuit((kMinInterval <= interval) && (interval <= kMaxInterval));
178 t = timer.GetTimeInInterval();
179 VerifyOrQuit((interval / 2 <= t) && (t <= interval));
180
181 // After `IndicateInconsistent()` should go back to min
182 // interval.
183
184 timer.IndicateInconsistent();
185
186 VerifyOrQuit(timer.IsRunning());
187 interval = timer.GetInterval();
188 VerifyOrQuit(interval == kMinInterval);
189 t = timer.GetTimeInInterval();
190 VerifyOrQuit((interval / 2 <= t) && (t <= interval));
191
192 for (uint8_t iter = 0; iter < 10; iter++)
193 {
194 for (uint32_t index = 0; index < aConsistentCalls; index++)
195 {
196 timer.IndicateConsistent();
197 }
198
199 AdvanceTime(t);
200
201 if (aConsistentCalls < aRedundancyConstant)
202 {
203 timer.VerifyTimerDidFire();
204 }
205 else
206 {
207 timer.VerifyTimerDidNotFire();
208 }
209
210 AdvanceTime(interval - t);
211
212 // Verify that interval is doubling each time up
213 // to max interval.
214
215 VerifyOrQuit(timer.IsRunning());
216 VerifyOrQuit(timer.GetInterval() == Min(interval * 2, kMaxInterval));
217
218 interval = timer.GetInterval();
219 t = timer.GetTimeInInterval();
220 VerifyOrQuit((interval / 2 <= t) && (t <= interval));
221 }
222
223 AdvanceTime(t);
224
225 timer.IndicateInconsistent();
226
227 VerifyOrQuit(timer.IsRunning());
228 interval = timer.GetInterval();
229 VerifyOrQuit(interval == kMinInterval);
230
231 printf(" --> PASSED\n");
232
233 testFreeInstance(instance);
234 }
235
TestTrickleTimerMinMaxIntervalChange(void)236 void TestTrickleTimerMinMaxIntervalChange(void)
237 {
238 Instance *instance = testInitInstance();
239 TrickleTimerTester timer(*instance);
240 TimeMilli fireTime;
241 uint32_t interval;
242 uint32_t t;
243
244 sInstance = instance;
245 TrickleTimerTester::RemoveAll(*instance);
246
247 printf("TestTrickleTimerMinMaxIntervalChange()");
248
249 sNow = 1000;
250 timer.Start(TrickleTimer::kModeTrickle, 2000, 4000);
251
252 VerifyOrQuit(timer.IsRunning());
253 VerifyOrQuit(timer.GetIntervalMin() == 2000);
254 VerifyOrQuit(timer.GetIntervalMax() == 4000);
255
256 //- - - - - - - - - - - - - - - - - - - - - - - - - - - -
257 // Validate that `SetIntervalMin()` to a larger value than
258 // previously set does not impact the current interval.
259
260 timer.IndicateInconsistent();
261 interval = timer.GetInterval();
262 t = timer.GetTimeInInterval();
263 fireTime = timer.GetFireTime();
264
265 VerifyOrQuit(interval == 2000);
266 VerifyOrQuit((interval / 2 <= t) && (t < interval));
267
268 // Change `IntervalMin` before time `t`.
269
270 timer.SetIntervalMin(3000);
271
272 VerifyOrQuit(timer.IsRunning());
273 VerifyOrQuit(timer.GetIntervalMin() == 3000);
274 VerifyOrQuit(timer.GetIntervalMax() == 4000);
275
276 VerifyOrQuit(interval == timer.GetInterval());
277 VerifyOrQuit(t == timer.GetTimeInInterval());
278 VerifyOrQuit(fireTime == timer.GetFireTime());
279
280 AdvanceTime(t);
281 timer.VerifyTimerDidFire();
282 fireTime = timer.GetFireTime();
283
284 // Change `IntervalMin` after time `t`.
285
286 timer.SetIntervalMin(3500);
287
288 VerifyOrQuit(timer.IsRunning());
289 VerifyOrQuit(timer.GetIntervalMin() == 3500);
290 VerifyOrQuit(timer.GetIntervalMax() == 4000);
291
292 VerifyOrQuit(interval == timer.GetInterval());
293 VerifyOrQuit(t == timer.GetTimeInInterval());
294 VerifyOrQuit(fireTime == timer.GetFireTime());
295
296 //- - - - - - - - - - - - - - - - - - - - - - - - - - - -
297 // Validate that `SetIntervalMin()` to a smaller value
298 // also does not impact the current interval.
299
300 timer.IndicateInconsistent();
301
302 interval = timer.GetInterval();
303 t = timer.GetTimeInInterval();
304 fireTime = timer.GetFireTime();
305
306 VerifyOrQuit(interval == 3500);
307 VerifyOrQuit((interval / 2 <= t) && (t < interval));
308
309 // Change `IntervalMin` before time `t`.
310
311 timer.SetIntervalMin(3000);
312
313 VerifyOrQuit(timer.IsRunning());
314 VerifyOrQuit(timer.GetIntervalMin() == 3000);
315 VerifyOrQuit(timer.GetIntervalMax() == 4000);
316
317 VerifyOrQuit(interval == timer.GetInterval());
318 VerifyOrQuit(t == timer.GetTimeInInterval());
319 VerifyOrQuit(fireTime == timer.GetFireTime());
320
321 AdvanceTime(t);
322 timer.VerifyTimerDidFire();
323 fireTime = timer.GetFireTime();
324
325 // Change `IntervalMin` after time `t`.
326
327 timer.SetIntervalMin(2000);
328
329 VerifyOrQuit(timer.IsRunning());
330 VerifyOrQuit(timer.GetIntervalMin() == 2000);
331 VerifyOrQuit(timer.GetIntervalMax() == 4000);
332
333 VerifyOrQuit(interval == timer.GetInterval());
334 VerifyOrQuit(t == timer.GetTimeInInterval());
335 VerifyOrQuit(fireTime == timer.GetFireTime());
336
337 //- - - - - - - - - - - - - - - - - - - - - - - - - - - -
338 // Validate that changing `IntervalMax` to a larger value
339 // than the current interval being used by timer, does not
340 // impact the current internal.
341
342 timer.IndicateInconsistent();
343
344 interval = timer.GetInterval();
345 t = timer.GetTimeInInterval();
346 fireTime = timer.GetFireTime();
347
348 VerifyOrQuit(interval == 2000);
349 VerifyOrQuit((interval / 2 <= t) && (t < interval));
350
351 // Change `IntervalMax` before time `t`.
352
353 timer.SetIntervalMax(2500);
354
355 VerifyOrQuit(timer.GetIntervalMax() == 2500);
356 VerifyOrQuit(timer.IsRunning());
357
358 VerifyOrQuit(interval == timer.GetInterval());
359 VerifyOrQuit(t == timer.GetTimeInInterval());
360 VerifyOrQuit(fireTime == timer.GetFireTime());
361
362 AdvanceTime(t);
363
364 timer.VerifyTimerDidFire();
365
366 fireTime = timer.GetFireTime();
367
368 // Change `IntervalMax` after time `t`.
369
370 timer.SetIntervalMax(3000);
371
372 VerifyOrQuit(interval == timer.GetInterval());
373 VerifyOrQuit(t == timer.GetTimeInInterval());
374 VerifyOrQuit(fireTime == timer.GetFireTime());
375
376 timer.Stop();
377 VerifyOrQuit(!timer.IsRunning());
378
379 //- - - - - - - - - - - - - - - - - - - - - - - - - - - -
380 // Check behavior when the new `IntervalMax` is smaller
381 // than the current interval being used by timer.
382
383 // New `Imax` is smaller than `t` and before now.
384 //
385 // |<---- interval --^-------------------------------->|
386 // |<---- t ---------^------------------>| |
387 // |<---- new Imax --^--->| | |
388 // | now | | |
389
390 timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
391 interval = timer.GetInterval();
392 t = timer.GetTimeInInterval();
393 fireTime = timer.GetFireTime();
394
395 VerifyOrQuit(interval == 2000);
396 VerifyOrQuit((interval / 2 <= t) && (t < interval));
397 timer.SetIntervalMin(500);
398
399 AdvanceTime(100);
400 timer.VerifyTimerDidNotFire();
401
402 timer.SetIntervalMax(500);
403
404 VerifyOrQuit(timer.GetInterval() == 500);
405 VerifyOrQuit(timer.GetTimeInInterval() == 500);
406 VerifyOrQuit(timer.GetFireTime() != fireTime);
407 timer.VerifyTimerDidNotFire();
408
409 AdvanceTime(400);
410 timer.VerifyTimerDidFire();
411
412 // New `Imax` is smaller than `t` and after now.
413 //
414 // |<---- interval --------------^-------------------->|
415 // |<---- t ---------------------^------>| |
416 // |<---- new Imax ------>| ^ | |
417 // | | now | |
418
419 timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
420 interval = timer.GetInterval();
421 t = timer.GetTimeInInterval();
422 fireTime = timer.GetFireTime();
423
424 VerifyOrQuit(interval == 2000);
425 VerifyOrQuit((interval / 2 <= t) && (t < interval));
426 timer.SetIntervalMin(500);
427
428 AdvanceTime(800);
429 timer.VerifyTimerDidNotFire();
430
431 timer.SetIntervalMax(500);
432
433 VerifyOrQuit(timer.GetInterval() == 500);
434 VerifyOrQuit(timer.GetTimeInInterval() == 500);
435 VerifyOrQuit(timer.GetFireTime() != fireTime);
436 timer.VerifyTimerDidNotFire();
437
438 AdvanceTime(0);
439 timer.VerifyTimerDidFire();
440
441 // New `Imax` is larger than `t` and before now.
442 //
443 // |<---- interval --------------------------------^-->|
444 // |<---- t ---------------------------->| ^ |
445 // |<---- new Imax --------------------------->| ^ |
446 // | | | now |
447
448 timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
449
450 interval = timer.GetInterval();
451 t = timer.GetTimeInInterval();
452
453 VerifyOrQuit(interval == 2000);
454 VerifyOrQuit((interval / 2 <= t) && (t < interval));
455 timer.SetIntervalMin(500);
456
457 AdvanceTime(1999);
458 timer.VerifyTimerDidFire();
459
460 timer.SetIntervalMax(t + 1);
461
462 VerifyOrQuit(timer.GetInterval() == t + 1);
463 fireTime = timer.GetFireTime();
464
465 // Check that new interval is started immediately.
466 AdvanceTime(0);
467 timer.VerifyTimerDidNotFire();
468 VerifyOrQuit(fireTime != timer.GetFireTime());
469 VerifyOrQuit(timer.GetInterval() == timer.GetIntervalMax());
470
471 // New `Imax` is larger than `t` and after now.
472 //
473 // |<---- interval -------------------------^--------->|
474 // |<---- t ---------------------------->| ^ |
475 // |<---- new Imax -------------------------^->| |
476 // | | now | |
477
478 timer.Start(TrickleTimer::kModeTrickle, 2000, 2000);
479
480 interval = timer.GetInterval();
481 t = timer.GetTimeInInterval();
482
483 VerifyOrQuit(interval == 2000);
484 VerifyOrQuit((interval / 2 <= t) && (t < interval));
485 timer.SetIntervalMin(500);
486
487 AdvanceTime(t);
488 timer.VerifyTimerDidFire();
489
490 timer.SetIntervalMax(t + 1);
491
492 VerifyOrQuit(timer.GetInterval() == t + 1);
493 fireTime = timer.GetFireTime();
494
495 AdvanceTime(1);
496 timer.VerifyTimerDidNotFire();
497 VerifyOrQuit(fireTime != timer.GetFireTime());
498 VerifyOrQuit(timer.GetInterval() == timer.GetIntervalMax());
499
500 printf(" --> PASSED\n");
501
502 testFreeInstance(instance);
503 }
504
505 } // namespace ot
506
main(void)507 int main(void)
508 {
509 ot::TestTrickleTimerPlainMode();
510 ot::TestTrickleTimerTrickleMode(/* aRedundancyConstant */ 5, /* aConsistentCalls */ 3);
511 ot::TestTrickleTimerTrickleMode(/* aRedundancyConstant */ 3, /* aConsistentCalls */ 3);
512 ot::TestTrickleTimerMinMaxIntervalChange();
513
514 printf("All tests passed\n");
515 return 0;
516 }
517