/*
 *  Copyright (c) 2016, The OpenThread Authors.
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#include "test_platform.h"

#include "common/array.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/num_utils.hpp"
#include "common/timer.hpp"
#include "instance/instance.hpp"

namespace ot {

enum
{
    kCallCountIndexAlarmStop = 0,
    kCallCountIndexAlarmStart,
    kCallCountIndexTimerHandler,

    kCallCountIndexMax
};

uint32_t sNow;
uint32_t sPlatT0;
uint32_t sPlatDt;
bool     sTimerOn;
uint32_t sCallCount[kCallCountIndexMax];

extern "C" {

void otPlatAlarmMilliStop(otInstance *)
{
    sTimerOn = false;
    sCallCount[kCallCountIndexAlarmStop]++;
}

void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
{
    sTimerOn = true;
    sCallCount[kCallCountIndexAlarmStart]++;
    sPlatT0 = aT0;
    sPlatDt = aDt;
}

uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }

#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
void otPlatAlarmMicroStop(otInstance *)
{
    sTimerOn = false;
    sCallCount[kCallCountIndexAlarmStop]++;
}

void otPlatAlarmMicroStartAt(otInstance *, uint32_t aT0, uint32_t aDt)
{
    sTimerOn = true;
    sCallCount[kCallCountIndexAlarmStart]++;
    sPlatT0 = aT0;
    sPlatDt = aDt;
}

uint32_t otPlatAlarmMicroGetNow(void) { return sNow; }
#endif

} // extern "C"

void InitCounters(void) { memset(sCallCount, 0, sizeof(sCallCount)); }

/**
 * `TestTimer` sub-classes `TimerMilli` and provides a handler and a counter to keep track of number of times timer
 * gets fired.
 */
template <typename TimerType> class TestTimer : public TimerType
{
public:
    explicit TestTimer(Instance &aInstance)
        : TimerType(aInstance, TestTimer::HandleTimerFired)
        , mFiredCounter(0)
    {
    }

    static void HandleTimerFired(Timer &aTimer) { static_cast<TestTimer &>(aTimer).HandleTimerFired(); }

    void HandleTimerFired(void)
    {
        sCallCount[kCallCountIndexTimerHandler]++;
        mFiredCounter++;
    }

    uint32_t GetFiredCounter(void) { return mFiredCounter; }

    void ResetFiredCounter(void) { mFiredCounter = 0; }

    static void RemoveAll(Instance &aInstance) { TimerType::RemoveAll(aInstance); }

private:
    uint32_t mFiredCounter; //< Number of times timer has been fired so far
};

template <typename TimerType> void AlarmFired(otInstance *aInstance);

template <> void AlarmFired<TimerMilli>(otInstance *aInstance) { otPlatAlarmMilliFired(aInstance); }

#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
template <> void AlarmFired<TimerMicro>(otInstance *aInstance) { otPlatAlarmMicroFired(aInstance); }
#endif

/**
 * Test the TimerScheduler's behavior of one timer started and fired.
 */
template <typename TimerType> int TestOneTimer(void)
{
    const uint32_t       kTimeT0        = 1000;
    const uint32_t       kTimerInterval = 10;
    Instance            *instance       = testInitInstance();
    TestTimer<TimerType> timer(*instance);

    // Test one Timer basic operation.

    TestTimer<TimerType>::RemoveAll(*instance);
    InitCounters();

    printf("TestOneTimer() ");

    sNow = kTimeT0;
    timer.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == 1000 && sPlatDt == 10, "Start params Failed.");
    VerifyOrQuit(timer.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval;

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    // Test one Timer that spans the 32-bit wrap.

    InitCounters();

    sNow = 0 - (kTimerInterval - 2);
    timer.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == 0 - (kTimerInterval - 2) && sPlatDt == 10, "Start params Failed.");
    VerifyOrQuit(timer.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval;

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    // Test one Timer that is late by several msec

    InitCounters();

    sNow = kTimeT0;
    timer.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == 1000 && sPlatDt == 10, "Start params Failed.");
    VerifyOrQuit(timer.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval + 5;

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    // Test one Timer that is early by several msec

    InitCounters();

    sNow = kTimeT0;
    timer.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == 1000 && sPlatDt == 10, "Start params Failed.");
    VerifyOrQuit(timer.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval - 2;

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(timer.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == true, "Platform Timer State Failed.");

    sNow += kTimerInterval;

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    printf(" --> PASSED\n");

    testFreeInstance(instance);

    return 0;
}

/**
 * Test the TimerScheduler's behavior of two timers started and fired.
 */
template <typename TimerType> int TestTwoTimers(void)
{
    const uint32_t       kTimeT0        = 1000;
    const uint32_t       kTimerInterval = 10;
    Instance            *instance       = testInitInstance();
    TestTimer<TimerType> timer1(*instance);
    TestTimer<TimerType> timer2(*instance);

    TestTimer<TimerType>::RemoveAll(*instance);
    printf("TestTwoTimers() ");

    // Test when second timer stars at the fire time of first timer (before alarm callback).

    InitCounters();

    sNow = kTimeT0;
    timer1.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == kTimeT0 && sPlatDt == kTimerInterval, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval;

    timer2.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == kTimeT0 && sPlatDt == kTimerInterval, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer1.GetFiredCounter() == 1, "Fire Counter failed.");
    VerifyOrQuit(sPlatT0 == sNow && sPlatDt == kTimerInterval, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == true, "Platform Timer State Failed.");

    sNow += kTimerInterval;
    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 2, "Handler CallCount Failed.");
    VerifyOrQuit(timer2.GetFiredCounter() == 1, "Fire Counter failed.");
    VerifyOrQuit(timer1.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    // Test when second timer starts at the fire time of first timer (before AlarmFired<TimerType>()) and its fire time
    // is before the first timer. Ensure that the second timer handler is invoked before the first one.

    InitCounters();
    timer1.ResetFiredCounter();
    timer2.ResetFiredCounter();

    sNow = kTimeT0;
    timer1.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == kTimeT0 && sPlatDt == kTimerInterval, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval;

    timer2.StartAt(TimeMilli(kTimeT0), kTimerInterval - 2); // Timer 2 is even before timer 1

    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(timer1.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer2.GetFiredCounter() == 1, "Fire Counter failed.");
    VerifyOrQuit(sPlatT0 == sNow && sPlatDt == 0, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == true, "Platform Timer State Failed.");

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 2, "Handler CallCount Failed.");
    VerifyOrQuit(timer1.GetFiredCounter() == 1, "Fire Counter failed.");
    VerifyOrQuit(timer1.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    // Timer 1 fire callback is late by some ticks/ms, and second timer is scheduled (before call to
    // AlarmFired) with a maximum interval. This is to test (corner-case) scenario where the fire time of two
    // timers spanning over the maximum interval.

    InitCounters();
    timer1.ResetFiredCounter();
    timer2.ResetFiredCounter();

    sNow = kTimeT0;
    timer1.Start(kTimerInterval);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == kTimeT0 && sPlatDt == kTimerInterval, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning(), "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    sNow += kTimerInterval + 5;

    timer2.Start(Timer::kMaxDelay);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 1, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "Handler CallCount Failed.");
    VerifyOrQuit(timer1.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(sTimerOn, "Platform Timer State Failed.");

    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 1, "Handler CallCount Failed.");
    VerifyOrQuit(timer1.GetFiredCounter() == 1, "Fire Counter failed.");
    VerifyOrQuit(sPlatT0 == sNow, "Start params Failed.");
    VerifyOrQuit(sPlatDt == Timer::kMaxDelay, "Start params Failed.");
    VerifyOrQuit(timer1.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == true, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == true, "Platform Timer State Failed.");

    sNow += Timer::kMaxDelay;
    AlarmFired<TimerType>(instance);

    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 1, "Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 2, "Handler CallCount Failed.");
    VerifyOrQuit(timer2.GetFiredCounter() == 1, "Fire Counter failed.");
    VerifyOrQuit(timer1.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(timer2.IsRunning() == false, "Timer running Failed.");
    VerifyOrQuit(sTimerOn == false, "Platform Timer State Failed.");

    printf(" --> PASSED\n");

    testFreeInstance(instance);

    return 0;
}

/**
 * Test the TimerScheduler's behavior of ten timers started and fired.
 *
 * `aTimeShift` is added to the t0 and trigger times for all timers. It can be used to check the ten timer behavior
 * at different start time (e.g., around a 32-bit wrap).
 */
template <typename TimerType> static void TenTimers(uint32_t aTimeShift)
{
    const uint32_t kNumTimers                 = 10;
    const uint32_t kNumTriggers               = 7;
    const uint32_t kTimeT0[kNumTimers]        = {1000, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008};
    const uint32_t kTimerInterval[kNumTimers] = {
        20, 100, (Timer::kMaxDelay - kTimeT0[2]), 100000, 1000000, 10, Timer::kMaxDelay, 200, 200, 200};
    // Expected timer fire order
    // timer #     Trigger time
    //   5            1014
    //   0            1020
    //   1            1100
    //   7            1206
    //   8            1207
    //   9            1208
    //   3          101002
    //   4         1001003
    //   2          kMaxDuration
    //   6   kMaxDuration + 1005
    const uint32_t kTriggerTimes[kNumTriggers] = {
        1014, 1020, 1100, 1207, 101004, Timer::kMaxDelay, Timer::kMaxDelay + kTimeT0[6]};
    // Expected timers fired by each kTriggerTimes[] value
    //  Trigger #    Timers Fired
    //    0             5
    //    1             0
    //    2             1
    //    3             7, 8
    //    4             9, 3
    //    5             4, 2
    //    6             6
    const bool kTimerStateAfterTrigger[kNumTriggers][kNumTimers] = {
        {true, true, true, true, true, false, true, true, true, true},         // 5
        {false, true, true, true, true, false, true, true, true, true},        // 0
        {false, false, true, true, true, false, true, true, true, true},       // 1
        {false, false, true, true, true, false, true, false, false, true},     // 7, 8
        {false, false, true, false, true, false, true, false, false, false},   // 9, 3
        {false, false, false, false, false, false, true, false, false, false}, // 4, 2
        {false, false, false, false, false, false, false, false, false, false} // 6
    };

    const bool kSchedulerStateAfterTrigger[kNumTriggers] = {true, true, true, true, true, true, false};

    const uint32_t kTimerHandlerCountAfterTrigger[kNumTriggers] = {1, 2, 3, 5, 7, 9, 10};

    const uint32_t kTimerStopCountAfterTrigger[kNumTriggers] = {0, 0, 0, 0, 0, 0, 1};

    const uint32_t kTimerStartCountAfterTrigger[kNumTriggers] = {3, 4, 5, 7, 9, 11, 11};

    Instance *instance = testInitInstance();

    TestTimer<TimerType>  timer0(*instance);
    TestTimer<TimerType>  timer1(*instance);
    TestTimer<TimerType>  timer2(*instance);
    TestTimer<TimerType>  timer3(*instance);
    TestTimer<TimerType>  timer4(*instance);
    TestTimer<TimerType>  timer5(*instance);
    TestTimer<TimerType>  timer6(*instance);
    TestTimer<TimerType>  timer7(*instance);
    TestTimer<TimerType>  timer8(*instance);
    TestTimer<TimerType>  timer9(*instance);
    TestTimer<TimerType> *timers[kNumTimers] = {&timer0, &timer1, &timer2, &timer3, &timer4,
                                                &timer5, &timer6, &timer7, &timer8, &timer9};
    size_t                i;

    printf("TestTenTimer() with aTimeShift=%-10u ", aTimeShift);

    // Start the Ten timers.

    TestTimer<TimerType>::RemoveAll(*instance);
    InitCounters();

    for (i = 0; i < kNumTimers; i++)
    {
        sNow = kTimeT0[i] + aTimeShift;
        timers[i]->Start(kTimerInterval[i]);
    }

    // given the order in which timers are started, the TimerScheduler should call otPlatAlarmMilliStartAt 2 times.
    // one for timer[0] and one for timer[5] which will supersede timer[0].
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == 2, "TestTenTimer: Start CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == 0, "TestTenTimer: Stop CallCount Failed.");
    VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == 0, "TestTenTimer: Handler CallCount Failed.");
    VerifyOrQuit(sPlatT0 == kTimeT0[5] + aTimeShift, "TestTenTimer: Start params Failed.");
    VerifyOrQuit(sPlatDt == kTimerInterval[5], "TestTenTimer: Start params Failed.");
    VerifyOrQuit(sTimerOn, "TestTenTimer: Platform Timer State Failed.");

    for (i = 0; i < kNumTimers; i++)
    {
        VerifyOrQuit(timers[i]->IsRunning(), "TestTenTimer: Timer running Failed.");
    }

    // Issue the triggers and test the State after each trigger.

    for (size_t trigger = 0; trigger < kNumTriggers; trigger++)
    {
        sNow = kTriggerTimes[trigger] + aTimeShift;

        do
        {
            // By design, each call to AlarmFired<TimerType>() can result in 0 or 1 calls to a timer handler.
            // For some combinations of sNow and Timers queued, it is necessary to call AlarmFired<TimerType>()
            // multiple times in order to handle all the expired timers.  It can be determined that another
            // timer is ready to be triggered by examining the aDt arg passed into otPlatAlarmMilliStartAt().  If
            // that value is 0, then AlarmFired should be fired immediately. This loop calls
            // AlarmFired<TimerType>() the requisite number of times based on the aDt argument.
            AlarmFired<TimerType>(instance);
        } while (sPlatDt == 0);

        VerifyOrQuit(sCallCount[kCallCountIndexAlarmStart] == kTimerStartCountAfterTrigger[trigger],
                     "TestTenTimer: Start CallCount Failed.");
        VerifyOrQuit(sCallCount[kCallCountIndexAlarmStop] == kTimerStopCountAfterTrigger[trigger],
                     "TestTenTimer: Stop CallCount Failed.");
        VerifyOrQuit(sCallCount[kCallCountIndexTimerHandler] == kTimerHandlerCountAfterTrigger[trigger],
                     "TestTenTimer: Handler CallCount Failed.");
        VerifyOrQuit(sTimerOn == kSchedulerStateAfterTrigger[trigger], "TestTenTimer: Platform Timer State Failed.");

        for (i = 0; i < kNumTimers; i++)
        {
            VerifyOrQuit(timers[i]->IsRunning() == kTimerStateAfterTrigger[trigger][i],
                         "TestTenTimer: Timer running Failed.");
        }
    }

    for (i = 0; i < kNumTimers; i++)
    {
        VerifyOrQuit(timers[i]->GetFiredCounter() == 1, "TestTenTimer: Timer fired counter Failed.");
    }

    printf("--> PASSED\n");

    testFreeInstance(instance);
}

template <typename TimerType> int TestTenTimers(void)
{
    // Time shift to change the start/fire time of ten timers.
    const uint32_t kTimeShift[] = {
        0, 100000U, 0U - 1U, 0U - 1100U, Timer::kMaxDelay, Timer::kMaxDelay + 1020U,
    };

    size_t i;

    for (i = 0; i < GetArrayLength(kTimeShift); i++)
    {
        TenTimers<TimerType>(kTimeShift[i]);
    }

    return 0;
}

/**
 * Test the `Timer::Time` class.
 */
int TestTimerTime(void)
{
    const uint32_t kMaxTime      = 0xffffffff;
    const uint32_t kStartTimes[] = {0, 100, kMaxTime / 2, kMaxTime - 100, kMaxTime};
    const uint32_t kDurations[]  = {1, 100, Timer::kMaxDelay - 1, Timer::kMaxDelay};

    Time t1;
    Time t2;

    for (uint32_t startTime : kStartTimes)
    {
        for (uint32_t duration : kDurations)
        {
            printf("TestTimerTime() start=%-10x  duration=%-10x ", startTime, duration);

            t1.SetValue(startTime);
            VerifyOrQuit(t1.GetValue() == startTime, "Time::SetValue() failed.");

            t2 = t1;
            VerifyOrQuit(t1.GetValue() == startTime, "Time assignment failed.");

            VerifyOrQuit(t1 == t2, "Time == failed.");
            VerifyOrQuit(!(t1 != t2), "Time != failed.");
            VerifyOrQuit(!(t1 < t2), "Time < failed.");
            VerifyOrQuit((t1 <= t2), "Time <= failed.");
            VerifyOrQuit(!(t1 > t2), "Time > failed.");
            VerifyOrQuit((t1 >= t2), "Time >= failed.");
            VerifyOrQuit(t2 - t1 == 0, "Time difference failed");

            t2 = t1 + duration;
            VerifyOrQuit(!(t1 == t2), "Time == failed.");
            VerifyOrQuit((t1 != t2), "Time != failed.");
            VerifyOrQuit((t1 < t2), "Time < failed.");
            VerifyOrQuit((t1 <= t2), "Time <= failed.");
            VerifyOrQuit(!(t1 > t2), "Time > failed.");
            VerifyOrQuit(!(t1 >= t2), "Time >= failed.");
            VerifyOrQuit(t2 - t1 == duration, "Time difference failed");

            t2 = t1;
            t2 += duration;
            VerifyOrQuit(!(t1 == t2), "Time == failed.");
            VerifyOrQuit((t1 != t2), "Time != failed.");
            VerifyOrQuit((t1 < t2), "Time < failed.");
            VerifyOrQuit((t1 <= t2), "Time <= failed.");
            VerifyOrQuit(!(t1 > t2), "Time > failed.");
            VerifyOrQuit(!(t1 >= t2), "Time >= failed.");
            VerifyOrQuit(t2 - t1 == duration, "Time difference failed");

            t2 = t1 - duration;
            VerifyOrQuit(!(t1 == t2), "Time == failed.");
            VerifyOrQuit((t1 != t2), "Time != failed.");
            VerifyOrQuit(!(t1 < t2), "Time < failed.");
            VerifyOrQuit(!(t1 <= t2), "Time <= failed.");
            VerifyOrQuit((t1 > t2), "Time > failed.");
            VerifyOrQuit((t1 >= t2), "Time >= failed.");
            VerifyOrQuit(t1 - t2 == duration, "Time difference failed");

            t2 = t1;
            t2 -= duration;
            VerifyOrQuit(!(t1 == t2), "Time == failed.");
            VerifyOrQuit((t1 != t2), "Time != failed.");
            VerifyOrQuit(!(t1 < t2), "Time < failed.");
            VerifyOrQuit(!(t1 <= t2), "Time <= failed.");
            VerifyOrQuit((t1 > t2), "Time > failed.");
            VerifyOrQuit((t1 >= t2), "Time >= failed.");
            VerifyOrQuit(t1 - t2 == duration, "Time difference failed");

            t2 = t1.GetDistantFuture();
            VerifyOrQuit((t1 < t2) && !(t1 > t2), "GetDistanceFuture() failed");
            t2 += 1;
            VerifyOrQuit(!(t1 < t2) || (t1 > t2), "GetDistanceFuture() failed");

            t2 = t1.GetDistantPast();
            VerifyOrQuit((t1 > t2) && !(t1 < t2), "GetDistantPast() failed");
            t2 -= 1;
            VerifyOrQuit(!(t1 > t2) || (t1 < t2), "GetDistantPast() failed");

            VerifyOrQuit(Min(t1, t1.GetDistantFuture()) == t1);
            VerifyOrQuit(Min(t1.GetDistantFuture(), t1) == t1);

            VerifyOrQuit(Max(t1, t1.GetDistantPast()) == t1);
            VerifyOrQuit(Max(t1.GetDistantPast(), t1) == t1);

            printf("--> PASSED\n");
        }
    }

    return 0;
}

template <typename TimerType> void RunTimerTests(void)
{
    TestOneTimer<TimerType>();
    TestTwoTimers<TimerType>();
    TestTenTimers<TimerType>();
}

} // namespace ot

int main(void)
{
    ot::RunTimerTests<ot::TimerMilli>();
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
    ot::RunTimerTests<ot::TimerMicro>();
#endif
    ot::TestTimerTime();
    printf("All tests passed\n");
    return 0;
}
