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