keepalive/flextimer/test/testflextimer/src/testflexperiodic.cpp
author hgs
Mon, 24 May 2010 20:51:35 +0300
changeset 32 5c4486441ae6
permissions -rw-r--r--
201021

/*
* ============================================================================
*  Name        : testflexperiodic.cpp
*  Part of     : src / testflextimer
*  Description : STIF test cases for CFlexPeriodic timer.
*  Version     : %version: 1 %
*
*  Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies).
*  All rights reserved.
*  This component and the accompanying materials are made available
*  under the terms of the License "Eclipse Public License v1.0"
*  which accompanies this distribution, and is available
*  at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
*  Initial Contributors:
*  Nokia Corporation - initial contribution.
*
*  Contributors:
*  Nokia Corporation
* ============================================================================
* Template version: 4.1
*/

#include <e32debug.h>               // for RDebug
#include <flexperiodic.h>           // for CFlexPeriodic
#include <stiftesteventinterface.h> // for TEventIf 
#include "testflextimer.h"          // for global constants
#include "testflexperiodic.h"

/**
 *  Timer can be expired 1 sec late
 *  
 *  Note! Definition
 *  
 *      const TTimeIntervalMicroSeconds32 KTimerResolution( 1000000 );
 * 
 * will cause writable static data due non-trivial constructor of 
 * TTimeIntervalMicroSeconds32.
 *  
 */
const TInt KTimerResolution( 1000000 );

// ======== LOCAL FUNCTIONS ========

// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
//
CTestFlexPeriodic::CTestFlexPeriodic()
    {
    }

// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CTestFlexPeriodic::~CTestFlexPeriodic()
    {
    }

// ---------------------------------------------------------------------------
// _ _ _ ____ _ ___    _    ____ ____ ___  
// | | | |__| |  |     |    |  | |  | |__] 
// |_|_| |  | |  |     |___ |__| |__| |    
//                                         
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Start scheduler for given time.
// ---------------------------------------------------------------------------
//
void CTestFlexPeriodic::WaitL( TTimeIntervalMicroSeconds32 aPeriod )
    {
    CPeriodic* watchdog = CPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( watchdog );
    watchdog->Start( aPeriod, aPeriod, TCallBack( StopScheduler, NULL ) );
    
    // Start scheduler. Wait here until the timer expires and stops the
    // scheduler.
    CActiveScheduler::Start();
    
    // Clean-up
    watchdog->Cancel();
    CleanupStack::PopAndDestroy( watchdog );
    }

// ---------------------------------------------------------------------------
// TCallBack function to stop the scheduler.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StopScheduler( TAny* /* aArgument */ )
    {
    CActiveScheduler::Stop();
    return KErrNone;
    }


// ---------------------------------------------------------------------------
// ____ ____ _    _    ___  ____ ____ _  _ ____ 
// |    |__| |    |    |__] |__| |    |_/  [__  
// |___ |  | |___ |___ |__] |  | |___ | \_ ___] 
//                                              
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// TCallBack function that does nothing. Not ment to be called - just to 
// fulfill the interface.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::DoNothing( TAny* /* aArgument */ )
    {
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TCallBack function that panics testcase if it get called.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::PanicClient( TAny* /* aArgument */ )
    {
    User::Panic(_L("NotCalled CB got called"), 0xDEAD);
    return KErrNone;
    }
// ---------------------------------------------------------------------------
// TCallBack function for adding time stamp to an RArray of TTimes.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::AddTimestamp( TAny* aArgument )
    {
    __ASSERT_ALWAYS(
        aArgument != NULL,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );

    //RDebug::Print( _L("CTestFlexPeriodic::AddTimestamp()") );
    
    RArray<TTime>* times =  reinterpret_cast<RArray<TTime>*>( aArgument );

    TTime now;
    now.UniversalTime();

    TInt err = times->Append( now ); // Data is copied by RArray
    __ASSERT_ALWAYS(
        err == KErrNone,
        User::Panic( KTestFlexTimerPanicCategory, err ) );

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TCallBack function for restarting CFlexPeriodic timer.
// Action depends the value of iFirstTicksLeft;
// >0 -- Add timestamp to iFirstTimestamps 
// <0 -- Add timestamp to iSecondTimestamps
// =0 -- Add timestamp to iFirstTimestamps AND iSecondTimestamps AND restart
//       the timer with iSecondInterval
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::RestartTimer( TAny* aArgument )
    {
    __ASSERT_ALWAYS(
        aArgument != NULL,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );

    TRestartInfo* info =  reinterpret_cast<TRestartInfo*>( aArgument );

    __ASSERT_ALWAYS(
        info->iTimer != NULL && 
        info->iFirstTimestamps != NULL && 
        info->iSecondTimestamps != NULL,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );
    
    // Add current time to timestamps
    // Data is copied by RArray - no need to use heap
    TTime now;
    now.UniversalTime();
    
    TInt ticks( info->iFirstTicksLeft-- ); // Update the ticks
    TInt err( KErrNone );
    if ( ticks > 0 )
        {
        err = info->iFirstTimestamps->Append( now );
        }
    else if ( ticks < 0 )
        {
        err = info->iSecondTimestamps->Append( now );
        }
    else // ticks == 0
        {
        // Set the timestamps.
        // 1st timer settings expiration time is the starting time of the
        // second timer settings. Add timestamp to both arrays.
        err = info->iFirstTimestamps->Append( now );
        __ASSERT_ALWAYS(
            err == KErrNone,
            User::Panic( KTestFlexTimerPanicCategory, err ) );
        err = info->iSecondTimestamps->Append( now );

        // Restart the timer
        info->iTimer->Cancel();
        info->iTimer->Start(
            info->iSecondInterval,
            info->iSecondInterval, 
            TCallBack( RestartTimer, info ) );
        }
    __ASSERT_ALWAYS(
        err == KErrNone,
        User::Panic( KTestFlexTimerPanicCategory, err ) );

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TCallBack function for trying to configure timer in callback.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureTimer( TAny* aArgument )
    {
    __ASSERT_ALWAYS(
        aArgument != NULL,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );

    TConfigureInfo* info =  reinterpret_cast<TConfigureInfo*>( aArgument );

    const TTimeIntervalMicroSeconds32 delayWindow32( info->iDelayWindow );
    const TTimeIntervalMicroSeconds32 intervalWindow32( info->iIntervalWindow );

    const TTimeIntervalMicroSeconds delayWindow64( info->iDelayWindow );
    const TTimeIntervalMicroSeconds intervalWindow64( info->iIntervalWindow );

    info->iConfigResult32 = info->iTimer->Configure( delayWindow32,
                                                     intervalWindow32);

    info->iConfigResult64 = info->iTimer->Configure( delayWindow64,
                                                     intervalWindow64);

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TCallBack function for starting a flexible periodic timer.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartTimer( TAny* aArgument )
    {
    __ASSERT_ALWAYS(
        aArgument != NULL,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );

    const TTimeIntervalMicroSeconds32 KTimerDelay( 2000000 );
    const TTimeIntervalMicroSeconds32 KTimerInterval( 2000000 );

    CFlexPeriodic* timer =  reinterpret_cast<CFlexPeriodic*>( aArgument );

    timer->Start(
        KTimerDelay,
        KTimerInterval,
        TCallBack( StartTimer, timer )
        );

    return KErrNone;
    }



// ---------------------------------------------------------------------------
// ___ _ _  _ ____ ____ ___ ____ _  _ ___     ____ _  _ _  _ ____ . ____ 
//  |  | |\/| |___ [__   |  |__| |\/| |__]    |___ |  | |\ | |    ' [__  
//  |  | |  | |___ ___]  |  |  | |  | |       |    |__| | \| |___   ___] 
//
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Verify that the timestamps are expired at window.
//
// Note that the timer resolution is 1 sec, so, the actual expiration time can
// be later than set.
// ---------------------------------------------------------------------------
//
TBool CTestFlexPeriodic::AreTimestampsAtWindow( 
    const RArray<TTime>& aTimestamps,
    const TTimeIntervalMicroSeconds32& aDelay,
    const TTimeIntervalMicroSeconds32& aInterval,
    const TTimeIntervalMicroSeconds32& aDelayWindow,
    const TTimeIntervalMicroSeconds32& aIntervalWindow )
    {
    __ASSERT_ALWAYS(
        aTimestamps.Count() >= 2 && 
        aDelay.Int() >= 0 &&
        aInterval.Int() >= 0 &&
        aDelayWindow.Int() >= 0 &&
        aIntervalWindow.Int() >= 0,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );

    // Timestamps are correct unless proved otherwise 
    TBool ret = ETrue;

    // For the 1st expiration is delay
    TTimeIntervalMicroSeconds delay;
    delay = aTimestamps[1].MicroSecondsFrom( aTimestamps[0] );
    RDebug::Print( _L("Timer delay %Ld"), delay.Int64() );

    if ( delay < aDelay.Int() - aDelayWindow.Int() || 
         delay > aDelay.Int() + KTimerResolution )
        {
        ret = EFalse;
        }

    // The rest of the expirations are intervals
    for ( TInt i( 2 ); i < aTimestamps.Count(); ++i )
        {
        TTimeIntervalMicroSeconds interval;
        interval = aTimestamps[i].MicroSecondsFrom( aTimestamps[i-1] );
        RDebug::Print( _L("Timer interval %Ld"), interval.Int64() );

        if ( interval < aInterval.Int() - aIntervalWindow.Int() || 
             interval > aInterval.Int() + KTimerResolution )
            {
            ret = EFalse;
            }
        }

    return ret;
    }

// ---------------------------------------------------------------------------
// Convert the intervals from 64 bit to 32 bit and call 32 bit checking 
// function.
// ---------------------------------------------------------------------------
//
TBool CTestFlexPeriodic::AreTimestampsAtWindow( 
    const RArray<TTime>& aTimestamps,
    const TTimeIntervalMicroSeconds& aDelay,
    const TTimeIntervalMicroSeconds& aInterval,
    const TTimeIntervalMicroSeconds& aDelayWindow,
    const TTimeIntervalMicroSeconds& aIntervalWindow )
    {
    __ASSERT_ALWAYS(
        I64HIGH( aDelay.Int64() ) == 0 &&
        I64HIGH( aInterval.Int64() ) == 0 &&
        I64HIGH( aDelayWindow.Int64() ) == 0 &&
        I64HIGH( aIntervalWindow.Int64() ) == 0,
        User::Panic( KTestFlexTimerPanicCategory, KErrArgument ) );

    TTimeIntervalMicroSeconds32 delay( I64INT( aDelay.Int64() ) );
    TTimeIntervalMicroSeconds32 interval( I64INT( aInterval.Int64() ) );
    TTimeIntervalMicroSeconds32 delayWindow( I64INT( aDelayWindow.Int64() ) );
    TTimeIntervalMicroSeconds32 intervalWindow( I64INT( aIntervalWindow.Int64() ) );

    return AreTimestampsAtWindow( aTimestamps, 
                                  delay,
                                  interval,
                                  delayWindow,
                                  intervalWindow );
    }

// ---------------------------------------------------------------------------
// Compare are timestamp arrays same
// ---------------------------------------------------------------------------
//
TBool CTestFlexPeriodic::AreTimestampsSame(
    const RArray<TTime>& aLeft,
    const RArray<TTime>& aRight )
    {
    TBool ret( ETrue );
    if ( aLeft.Count() == aRight.Count() )
        {
        for ( TInt i( 0 ); i < aLeft.Count(); ++i )
            {
            if ( aLeft[i] != aRight[i] )
                { // Different timestamp has been found
                ret = EFalse;
                break;
                }
            }
        }
    else
        { // Arrays has different number of timestamps.
        ret = EFalse;
        }

    return ret;
    }

// ---------------------------------------------------------------------------
// ___ ____ _  _ ___  _    ____ ___ ____    ____ _  _ _  _ ____ . ____ 
//  |  |___ |\/| |__] |    |__|  |  |___    |___ |  | |\ | |    ' [__  
//  |  |___ |  | |    |___ |  |  |  |___    |    |__| | \| |___   ___] 
//
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Configure window sizes - template function
// 
// Timers are designed so that the 1st timer will test that the delay is
// correct and the 2nd timer will test that the interval is correct
// ---------------------------------------------------------------------------
template <class firstType, class secondType>
TInt CTestFlexPeriodic::ConfigureWindowL( TTestResult& aResult )
    {
    __UHEAP_MARK;

    // Constants
    const firstType KFirstDelay( 3000000 );             // 3 sec
    const firstType KFirstInterval( 2000000 );          // 2 sec
    const firstType KFirstDelayWindow( 0 );             // no window
    const firstType KFirstIntervalWindow( 1500000 );    // 1.5 sec

    const secondType KSecondDelay( 3500000 );           // 3.5 sec
    const secondType KSecondInterval( 1000000 );        // 1 sec
    const secondType KSecondDelayWindow( 1500000 );     // 1.5 sec
    const secondType KSecondIntervalWindow( 0 );        // no window

    const TUint KTestRunTime( 10000000 ); // 10 sec

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // Create, configure and start the flexible periodic timer
    RArray<TTime> firstTimestamps;
    CFlexPeriodic* firstTimer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( firstTimer );
    firstTimer->Configure( KFirstDelayWindow, KFirstIntervalWindow );
    firstTimer->Start( 
        KFirstDelay, 
        KFirstInterval, 
        TCallBack( AddTimestamp, &firstTimestamps ) );

    RArray<TTime> secondTimestamps;
    CFlexPeriodic* secondTimer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( secondTimer );
    secondTimer->Configure( KSecondDelayWindow, KSecondIntervalWindow );
    secondTimer->Start( 
        KSecondDelay, 
        KSecondInterval,
        TCallBack( AddTimestamp, &secondTimestamps ) );

    // Initialize timer expiration time array with starting time to 
    // ease analysing of data.
    TTime startTime;
    startTime.UniversalTime();
    firstTimestamps.Append( startTime );
    secondTimestamps.Append( startTime );

    // The execution will be pending here while active scheduler is running...

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // Analyze results
    aResult.SetResult( KErrNone, _L("Test case passed") );

    // Check the 1st timer expiration time
    if ( !AreTimestampsAtWindow( 
            firstTimestamps, 
            KFirstDelay, 
            KFirstInterval, 
            KFirstDelayWindow, 
            KFirstIntervalWindow ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. First timer not in window.") );
        }

    // Check the 2nd timer expiration time
    else if ( !AreTimestampsAtWindow( 
            secondTimestamps, 
            KSecondDelay, 
            KSecondInterval, 
            KSecondDelayWindow, 
            KSecondIntervalWindow ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Second timer not in window.") );
        }
    
    // Check that both timers are expired at the same time
    else if ( !AreTimestampsSame( firstTimestamps, secondTimestamps ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Timers are not expired at the same time.") );
        }

    // Clean up
    secondTimer->Cancel();
    secondTimestamps.Close();
    CleanupStack::PopAndDestroy( secondTimer );

    firstTimer->Cancel();
    firstTimestamps.Close();
    CleanupStack::PopAndDestroy( firstTimer );
    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Configure window sizes and start timer with given values - template 
// function
// ---------------------------------------------------------------------------
template <class configureType, class startType>
void CTestFlexPeriodic::ConfigureAndStartTimerL( 
    RArray<TTime>& aTimestamps,
    TInt64 aDelay,
    TInt64 aInterval,
    TInt64 aDelayWindow,
    TInt64 aIntervalWindow )
    {
    const startType KDelay( aDelay );
    const startType KInterval( aInterval );
    const configureType KDelayWindow( aDelayWindow );
    const configureType KIntervalWindow( aIntervalWindow );

    const TUint KTestRunTime( 3000000 );

    TTime now;
    now.UniversalTime();
    aTimestamps.Append( now );

    // Create and start the flexible periodic timer
    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );
    timer->Configure( KDelayWindow, KIntervalWindow );
    timer->Start( KDelay, KInterval, TCallBack( AddTimestamp, &aTimestamps ) );
    
    // The execution will be pending here while active scheduler is running...

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // Clean up
    timer->Cancel();
    CleanupStack::PopAndDestroy( timer );
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start timer with NULL callback 
// ---------------------------------------------------------------------------
//
template<class startType>
TInt CTestFlexPeriodic::StartWithNullCallBackL(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback )
    {
    __UHEAP_MARK;

    // Constants
    const startType KTimerDelay( 1000000 );
    const startType KTimerInterval( 1000000 );

    TInt(* nullCallback)(TAny*) = NULL;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   31 (EFlexPeriodicCallbackFunctionIsNull)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 31 );
    
    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );
    timer->Start( 
        KTimerDelay, 
        KTimerInterval, 
        TCallBack( nullCallback, NULL ) );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timer->Cancel();
    CleanupStack::PopAndDestroy( timer );

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// Panic thread after a few seconds
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::DoPanicL(
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */ )
    {
    __UHEAP_MARK;

    // Constants
    const TTimeIntervalMicroSeconds32 KTimerDelay( 1000000 );
    const TTimeIntervalMicroSeconds32 KTimerInterval( 1000000 );
    const TTimeIntervalMicroSeconds32 KTestRunTime( 3000000 );

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );
    timer->Start( 
        KTimerDelay, 
        KTimerInterval, 
        TCallBack( DoNothing, NULL ) );

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    User::Panic(_L("Die die die!"), 0xDEAD);

    // We should NEVER be here...
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timer->Cancel();
    CleanupStack::PopAndDestroy( timer );

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// ____ _  _ _  _    _ _  _    ___ _  _ ____ ____ ____ ___  
// |__/ |  | |\ |    | |\ |     |  |__| |__/ |___ |__| |  \ 
// |  \ |__| | \|    | | \|     |  |  | |  \ |___ |  | |__/ 
//
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Run test case in own thread
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::RunInThread( 
    RThread& aThread, 
    TTestCaseArguments& aArguments )
    {
    // RThread::Create() parameters
    const TInt stackSize = 1024;
    const TInt heapMinSize = 1024;
    const TInt heapMaxSize = 1024;

    TBuf<8> processName;
    processName.Format( _L("%x"), &aArguments.iResult );
    
    // Create the thread
    TInt ret = aThread.Create(
        processName,
        RunTestCase, 
        stackSize,
        heapMinSize,
        heapMaxSize,
        &aArguments,
        EOwnerProcess );

    // Start execution of the thread
    aThread.Resume();

    return ret;
    }

// ---------------------------------------------------------------------------
// Create cleanup stack
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::RunTestCase( TAny* aArgument )
    {
    CTrapCleanup* cleanup = CTrapCleanup::New();

    // Out of memory assert
    __ASSERT_ALWAYS(
        cleanup != NULL, 
        User::Panic( KTestFlexTimerPanicCategory, KErrNoMemory ) );
    
    TRAPD( err, CTestFlexPeriodic::RunTestCaseL( aArgument ) );

    delete cleanup;
    return err;
    }

// ---------------------------------------------------------------------------
// Create scheduler and run the test case
// ---------------------------------------------------------------------------
//
void CTestFlexPeriodic::RunTestCaseL( TAny* aArgument )
    {
    // Create and install active scheduler
    CActiveScheduler* scheduler = scheduler = new (ELeave) CActiveScheduler;
    CleanupStack::PushL( scheduler );
    CActiveScheduler::Install( scheduler );
    
    // Parse the arguments
    TTestCaseArguments* args = reinterpret_cast<TTestCaseArguments*>( aArgument );
    
    // Call the function pointer with given arguments
    TInt ret = (*(args->iTestFunction))(args->iResult, args->iCallback );
    User::LeaveIfError( ret );

    // Clean up
    CleanupStack::PopAndDestroy( scheduler );
    }

// ---------------------------------------------------------------------------
// ___ ____ ____ ___    ____ ____ ____ ____ ____ 
//  |  |___ [__   |     |    |__| [__  |___ [__  
//  |  |___ ___]  |     |___ |  | ___] |___ ___] 
//
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// TEST CASE: Start one CFlexPeriodic and wait for few expirations.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartOneTimerL( 
        TTestResult& aResult, 
        CTestFlexTimer* /* aCallback */ )
    {
    __UHEAP_MARK;

    // Constants
    const TTimeIntervalMicroSeconds32 KTickInterval( 1000000 ); // 1 sec
    const TUint KTestRunTime( 5000000 );

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // Storage for flexible periodic timer timestamps
    RArray<TTime> timestamps;

    // Create and start the flexible periodic timer
    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );
    timer->Start( 
        KTickInterval, 
        KTickInterval, 
        TCallBack( AddTimestamp, &timestamps ) );

    // Initialize timer expiration time array with starting time to 
    // ease analysing of data.
    TTime startTime;
    startTime.UniversalTime();
    timestamps.Append( startTime );
    
    // The execution will be pending here while active scheduler is running...

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // Analyze results
    aResult.SetResult( KErrNone, _L("Test case passed") );

    // Only start time in timestamp array
    if ( timestamps.Count() == 1 )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. No timer expired.") );
        }

    // Check that the timers are expired at maximum delay due there are only
    // one timer, so, no alignment can be happened.
    if ( !AreTimestampsAtWindow( timestamps, KTickInterval, KTickInterval, 0, 0 ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Timer not in maximum window.") );
        }

    // Clean up
    timer->Cancel();
    timestamps.Close();
    CleanupStack::PopAndDestroy( timer );
    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start one CFlexPeriodic and wait for few expirations.
// Give also Error callback function.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartOneTimerWithErrorCbL( 
        TTestResult& aResult, 
        CTestFlexTimer* /* aCallback */ )
    {
    __UHEAP_MARK;

    // Constants
    const TTimeIntervalMicroSeconds32 KTickInterval( 1000000 ); // 1 sec
    const TUint KTestRunTime( 5000000 );

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // Storage for flexible periodic timer timestamps
    RArray<TTime> timestamps;

    // Create and start the flexible periodic timer
    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );
    timer->Start( 
        KTickInterval, 
        KTickInterval, 
        TCallBack( AddTimestamp, &timestamps ),
        TCallBack( PanicClient, NULL ) );

    // Initialize timer expiration time array with starting time to 
    // ease analysing of data.
    TTime startTime;
    startTime.UniversalTime();
    timestamps.Append( startTime );
    
    // The execution will be pending here while active scheduler is running...

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // Analyze results
    aResult.SetResult( KErrNone, _L("Test case passed") );

    // Only start time in timestamp array
    if ( timestamps.Count() == 1 )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. No timer expired.") );
        }

    // Check that the timers are expired at maximum delay due there are only
    // one timer, so, no alignment can be happened.
    if ( !AreTimestampsAtWindow( timestamps, KTickInterval, KTickInterval, 0, 0 ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Timer not in maximum window.") );
        }

    // Clean up
    timer->Cancel();
    timestamps.Close();
    CleanupStack::PopAndDestroy( timer );
    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start one CFlexPeriodic, cancel it and restart it in callback
// function.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::CallbackRestartL( 
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */ )
    {
    __UHEAP_MARK;

    // Constants
    const TTimeIntervalMicroSeconds KTickInterval( 1000000 ); // 1 sec
    const TTimeIntervalMicroSeconds KTick2ndInterval( 2000000 ); // 2 sec
    const TUint KTestRunTime( 10000000 );

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    RArray<TTime> firstTimestamps;
    RArray<TTime> secondTimestamps;

    TRestartInfo info;
    info.iTimer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    info.iFirstTicksLeft = 1;
    info.iFirstTimestamps = &firstTimestamps;
    info.iSecondTimestamps = &secondTimestamps;
    info.iSecondInterval = KTick2ndInterval;

    // Create and start the flexible periodic timer
    CleanupStack::PushL( info.iTimer );
    info.iTimer->Start( 
        KTickInterval, 
        KTickInterval, 
        TCallBack( RestartTimer, &info ) );

    // Initialize timer expiration time array with starting time to 
    // ease analysing of data.
    TTime startTime;
    startTime.UniversalTime();
    firstTimestamps.Append( startTime );
    
    // The execution will be pending here while active scheduler is running...

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // Analyze results
    aResult.SetResult( KErrNone, _L("Test case passed") );

    // Check the 1st timer expiration time
    if ( !AreTimestampsAtWindow( firstTimestamps, KTickInterval, KTickInterval, 0, 0 ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Pre-reset timer not in maximum window.") );
        }
    
    // Check the rest of timers
    else if ( !AreTimestampsAtWindow( secondTimestamps, KTick2ndInterval, KTick2ndInterval, 0, 0 ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Post-reset timer not in maximum window.") );
        }
    
    // Clean up
    info.iTimer->Cancel();
    firstTimestamps.Close();
    secondTimestamps.Close();
    CleanupStack::PopAndDestroy( info.iTimer );
    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure window sizes - 32 bit
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWindow32L( 
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */ )
    {
    return ConfigureWindowL<TTimeIntervalMicroSeconds32,TTimeIntervalMicroSeconds32>( aResult );
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure window sizes - 64 bit
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWindow64L(
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */ )
    {
    return ConfigureWindowL<TTimeIntervalMicroSeconds,TTimeIntervalMicroSeconds>( aResult );
    }

// ---------------------------------------------------------------------------
// OK TEST CASE: Configure window sizes - mixed 32 bit and 64 bit
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWindowMixL(
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */ )
    {
    return ConfigureWindowL<TTimeIntervalMicroSeconds32,TTimeIntervalMicroSeconds>( aResult );
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure after timer has started 
// The test case is divided into following parts:
// 1) Remove slack from the heartbeat to make it sure that timer's default
//    windows does not cause the drifting of the timer.
// 2) Start timer, try to configure it, verify that configure has not been
//    changed (no alignment should be made by heartbeat)
// 3) Cancel the timer and start it with a callback that tries to configure
//    it while in callback, check the results
// 4) Verify that the configuration stil works by happened timer alignment
//    by the heartbeat
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureAfterStartL(
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */ )
    {
    __UHEAP_MARK;

    // Constants
    
    // Heartbeat timer
    // Use 1us to adjust the engine's timer. 0 returns immediately.
    const TTimeIntervalMicroSeconds32 KHeartbeatDelay( 1 );
    const TTimeIntervalMicroSeconds32 KHeartbeatInterval( 1000000 );
    const TTimeIntervalMicroSeconds32 KHeartbeatDelayWindow( 0 );
    const TTimeIntervalMicroSeconds32 KHeartbeatIntervalWindow( 0 );

    // Timer under test
    const TTimeIntervalMicroSeconds32 KTimerDelayNow( 0 );
    const TTimeIntervalMicroSeconds32 KTimerDelay( 2000000 );
    const TTimeIntervalMicroSeconds32 KTimerInterval( 2000000 );
    const TTimeIntervalMicroSeconds32 KTimerInitialDelayWindow( 0 );
    const TTimeIntervalMicroSeconds32 KTimerInitialIntervalWindow( 0 );

    const TTimeIntervalMicroSeconds32 KTimerDelayWindow32( 1500000 );
    const TTimeIntervalMicroSeconds32 KTimerIntervalWindow32( 1500000 );

    const TTimeIntervalMicroSeconds KTimerDelayWindow64( 1500000 );
    const TTimeIntervalMicroSeconds KTimerIntervalWindow64( 1500000 );
    
    const TUint KConfigNokTestRunTime( 7000000 );
    const TUint KTestCbConfigRunTime( KHeartbeatInterval.Int() );
    const TUint KConfigOkTestRunTime( 4000000 );
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );
    
    // Create, configure and initialize and start the heartbeat
    // This timer is used for checking that the timer under test is reacting
    // correctly to configurations.
    RArray<TTime> heartbeatTimes;
    CFlexPeriodic* heartbeat = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( heartbeat );
    heartbeat->Configure( KHeartbeatDelayWindow, KHeartbeatIntervalWindow );
    heartbeat->Start( 
        KHeartbeatDelay, 
        KHeartbeatInterval, 
        TCallBack( AddTimestamp, &heartbeatTimes ) );

    // Remove the slack from timer start up -- wait till next second
    const TInt64 KSecondInMicroSeconds( 1000000 );
    TTime now;
    now.UniversalTime();
    heartbeatTimes.Append( now );
    TUint slack( I64LOW( KSecondInMicroSeconds - now.Int64() % KSecondInMicroSeconds ) );

                     //    //  ___     _____
    WaitL( slack ); // // // //_ // //  //
                   //_//_// //  // //  //

    // Create and start the flexible periodic timer
    RArray<TTime> timestamps;
    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );
    timer->Configure( KTimerInitialDelayWindow, KTimerInitialIntervalWindow );
    timer->Start( 
        KTimerDelay, 
        KTimerInterval, 
        TCallBack( AddTimestamp, &timestamps ) );

    aResult.SetResult( KErrNone, _L("Test case passed") );

    if ( timer->Configure( KTimerDelayWindow32, KTimerIntervalWindow32 ) != KErrInUse )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. 32 bit configure didn't return KErrInUse") );
        }
    else if ( timer->Configure( KTimerDelayWindow64, KTimerIntervalWindow64 ) != KErrInUse )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. 64 bit configure didn't return KErrInUse") );
        }

    // Initialize timer expiration time array with starting time to 
    // ease analysing of data.
    now.UniversalTime();
    timestamps.Append( now );
                                     //    //  ___     _____
    WaitL( KConfigNokTestRunTime ); // // // //_ // //  //
                                   //_//_// //  // //  //

    // Check the 1st timer expiration time
    RDebug::Print( _L("Timer:") );
    if ( !AreTimestampsAtWindow( 
            timestamps, 
            KTimerDelay,
            KTimerInterval,
            KTimerInitialDelayWindow,
            KTimerInitialIntervalWindow ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Configuration changed after start. ") );
        }

    // Restart the timer to try configuration while callback
    timer->Cancel();

    TConfigureInfo configInfo;
    configInfo.iTimer = timer;
    configInfo.iDelayWindow = KTimerDelayWindow32.Int();
    configInfo.iIntervalWindow = KTimerIntervalWindow32.Int();
    configInfo.iConfigResult32 = 0xDEADBEEF; // Initialize result
    configInfo.iConfigResult64 = 0xDEADBEEF; // Initialize result

    timer->Start(
        KTimerDelayNow, 
        KTimerInterval, 
        TCallBack( ConfigureTimer, &configInfo ) );

                                    //    //  ___     _____
    WaitL( KTestCbConfigRunTime ); // // // //_ // //  //
                                  //_//_// //  // //  //

    timer->Cancel();

    RDebug::Print( _L("configInfo.iConfigResult32=%d (0x%x)"), configInfo.iConfigResult32, configInfo.iConfigResult32 );
    RDebug::Print( _L("configInfo.iConfigResult64=%d (0x%x)"), configInfo.iConfigResult64, configInfo.iConfigResult64 );

    // Analyze the results
    if ( configInfo.iConfigResult32 != KErrInUse )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. 32 bit configure in callback didn't return KErrInUse") );
        }
    else if ( configInfo.iConfigResult64 != KErrInUse )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. 64 bit configure in callback didn't return KErrInUse") );
        }

    // Test that the configuration still works
    RArray<TTime> secondTimestamps;
    now.UniversalTime();
    secondTimestamps.Append( now );

    if ( timer->Configure( KTimerDelayWindow32, KTimerIntervalWindow32 ) != KErrNone )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Configure failed.") );
        }
    else
        {
        timer->Start(
            KTimerDelay,
            KTimerInterval,
            TCallBack( AddTimestamp, &secondTimestamps ));

                                        //    //  ___     _____
        WaitL( KConfigOkTestRunTime ); // // // //_ // //  //
                                      //_//_// //  // //  //

        // Check timestamps, they should be the same as heartbeat
        RDebug::Print( _L("secondTimestamps:") );
        if ( !AreTimestampsAtWindow( 
                secondTimestamps, 
                KHeartbeatInterval, // Heartbeat was running already 
                KHeartbeatInterval,
                KHeartbeatInterval, // There can be adjustement with 1st expiration 
                0 ) )
            {
            aResult.SetResult( 
                KErrGeneral, 
                _L("Test case failed. Configure does not work.") );
            }
        }

    RDebug::Print( _L("Heartbeat:") );
    if ( !AreTimestampsAtWindow( 
            heartbeatTimes, 
            KHeartbeatDelay,
            KHeartbeatInterval,
            KHeartbeatDelayWindow,
            KHeartbeatIntervalWindow ) )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Heartbeat failure.") );
        }

    // Clean up
    timestamps.Close();
    secondTimestamps.Close();
    timer->Cancel();
    CleanupStack::PopAndDestroy( timer );

    heartbeatTimes.Close();
    heartbeat->Cancel();
    CleanupStack::PopAndDestroy( heartbeat );

    __UHEAP_MARKEND;

    return KErrNone;
    }


// ---------------------------------------------------------------------------
// TEST CASE: Start a running timer again.
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartAfterStartL(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    // Constants
    const TTimeIntervalMicroSeconds32 KTimerDelay( 2000000 );
    const TTimeIntervalMicroSeconds32 KTimerInterval( 2000000 );

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );

    // This start should work...
    timer->Start( 
        KTimerDelay, 
        KTimerInterval, 
        TCallBack( DoNothing, NULL ) );

    // ... and next start should panic with
    // Panic category: "E32USER-CBase"
    // Panic reason:   42 (attempt to active CActive when a request is still 
    //                     outstanding)
    
    // Set panic code 42 to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( 
        CTestModuleIf::EPanic,
        42 );

    timer->Start( 
        KTimerDelay, 
        KTimerInterval, 
        TCallBack( DoNothing, NULL ) );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( 
        CTestModuleIf::ENormal,
        KErrNone );

    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );

    // Clean up
    timer->Cancel();
    CleanupStack::PopAndDestroy( timer );

    __UHEAP_MARKEND;    

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start a running timer again in callback function
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartInCallbackL(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    // Constants
    const TTimeIntervalMicroSeconds32 KTimerDelay( 1000000 );
    const TTimeIntervalMicroSeconds32 KTimerInterval( 1000000 );
    const TTimeIntervalMicroSeconds32 KTestRunTime( 3000000 );
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    CFlexPeriodic* timer = CFlexPeriodic::NewL( CActive::EPriorityStandard );
    CleanupStack::PushL( timer );

    // The callback should panic with
    // Panic category: "E32USER-CBase"
    // Panic reason:   42 (attempt to active CActive when a request is still 
    //                     outstanding)
    
    // Set panic code 42 to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( 
        CTestModuleIf::EPanic,
        42 );

    timer->Start( 
        KTimerDelay, 
        KTimerInterval, 
        TCallBack( StartTimer, timer ) );

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );

    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );

    // Clean up
    timer->Cancel();
    CleanupStack::PopAndDestroy( timer );

    __UHEAP_MARKEND;    

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with negative delay, 32 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithNegativeDelay32L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );

    RArray<TTime> timestamps;

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   6 (EFlexPeriodicDelayLessThanZero)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 6 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();
    
    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with zero interval, 32 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithZeroInterval32L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( 0 );

    RArray<TTime> timestamps;

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   7 (EFlexPeriodicIntervalTooSmall)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 7 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with negative interval, 32 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithNegativeInterval32L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );

    RArray<TTime> timestamps;

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   7 (EFlexPeriodicIntervalTooSmall)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 7 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with negative delay, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithNegativeDelay64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );

    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   6 (EFlexPeriodicDelayLessThanZero)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 6 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with zero interval, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithZeroInterval64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( 0 );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   7 (EFlexPeriodicIntervalTooSmall)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 7 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with negative interval, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithNegativeInterval64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   7 (EFlexPeriodicIntervalTooSmall)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 7 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure timer with negative delay window, 32 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWithNegativeDelayWindow32L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   8 (EFlexPeriodicDelayWindowLessThanZero)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 8 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KAllowedValue,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure timer with negative interval window, 32 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWithNegativeIntervalWindow32L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   9 (EFlexPeriodicIntervalWindowLessThanZero)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 9 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KAllowedValue,
        KAllowedValue,
        KAllowedValue,
        KIllegalValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure timer with negative delay window, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWithNegativeDelayWindow64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   8 (EFlexPeriodicDelayWindowLessThanZero)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 8 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KAllowedValue,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Configure timer with negative interval window, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ConfigureWithNegativeIntervalWindow64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt KIllegalValue( -1 );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   9 (EFlexPeriodicIntervalWindowLessThanZero)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 9 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KAllowedValue,
        KAllowedValue,
        KAllowedValue,
        KIllegalValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start with minimum and maximum values
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithMinAndMaxL(
    TTestResult& aResult, 
    CTestFlexTimer* /* aCallback */  )
    {
    __UHEAP_MARK;

    const TInt KMinimumDelayValue( 0 );
    const TInt KMinimumIntervalValue( 1 );
    const TInt KNormalValue( 1000000 );
    const TInt KMaximum32BitValue( 0x7FFFFFFF );

//    const TInt64 KLarge64BitValue( 0x6FFFFFFFFFFFFFFF );  // Should not panic (till few years)

    // Years * Days * Hours * Minutes * Seconds * Microseconds
    const TInt64 KLarge64BitValue( ((TInt64)(1)) * 365 * 24 * 60 * 60 * 1000000 );
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );
    
    RArray<TTime> timestamps;

    TTime now;
    now.UniversalTime();

    // > DEBUG
    TTimeIntervalMicroSeconds longLongInterval( KLarge64BitValue );
    now += longLongInterval;
    TDateTime date;
    date = now.DateTime();
    // < DEBUG

    // Start with min delay, 32 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KMinimumDelayValue,
        KNormalValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    if ( !AreTimestampsAtWindow(
            timestamps,
            TTimeIntervalMicroSeconds32( KMinimumDelayValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ) ) )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 32 bit min delay failed") );
        }
    timestamps.Reset();

    // Start with max delay, 32 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KMaximum32BitValue,
        KNormalValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    // Only one timestamp (the start time) is allowed
    if ( timestamps.Count() > 1 )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 32 bit max delay failed") );
        }
    timestamps.Reset();

    // Start with min interval, 32 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KNormalValue,
        KMinimumIntervalValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    if ( !AreTimestampsAtWindow(
            timestamps,
            TTimeIntervalMicroSeconds32( KNormalValue ),
            TTimeIntervalMicroSeconds32( KMinimumIntervalValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ) ) )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 32 bit min interval failed") );
        }
    timestamps.Reset();

    // Start with max interval, 32 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds32, TTimeIntervalMicroSeconds32>(
        timestamps,
        KNormalValue,
        KMaximum32BitValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    if ( !AreTimestampsAtWindow(
            timestamps,
            TTimeIntervalMicroSeconds32( KNormalValue ),
            TTimeIntervalMicroSeconds32( KMaximum32BitValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ),
            TTimeIntervalMicroSeconds32( KNormalValue ) ) )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 32 bit min interval failed") );
        }
    timestamps.Reset();

    // Start with min delay, 64 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KMinimumDelayValue,
        KNormalValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    if ( !AreTimestampsAtWindow(
            timestamps,
            TTimeIntervalMicroSeconds( KMinimumDelayValue ),
            TTimeIntervalMicroSeconds( KNormalValue ),
            TTimeIntervalMicroSeconds( KNormalValue ),
            TTimeIntervalMicroSeconds( KNormalValue ) ) )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 64 bit min delay failed") );
        }
    timestamps.Reset();

    // Start with large delay, 64 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KLarge64BitValue,
        KNormalValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    // Only one timestamp (the start time) is allowed
    if ( timestamps.Count() > 1 )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 64 bit max delay failed") );
        }
    timestamps.Reset();

    // Start with min interval, 64 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KNormalValue,
        KMinimumIntervalValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    if ( !AreTimestampsAtWindow(
            timestamps,
            TTimeIntervalMicroSeconds( KNormalValue ),
            TTimeIntervalMicroSeconds( KMinimumIntervalValue ),
            TTimeIntervalMicroSeconds( KNormalValue ),
            TTimeIntervalMicroSeconds( KNormalValue ) ) )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 64 bit min interval failed") );
        }
    timestamps.Reset();

    // Start with large interval, 64 bit
    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KNormalValue,
        KLarge64BitValue,
        KNormalValue,
        KNormalValue );
    // Validate results
    // AreTimestampsAtWindow() does not accept values over 0xFFFFFFFF.
    // However, this is not a problem due there should be only the timestamps
    // of start time and delay expiration; 32 bit value can be used instead of
    // the real interval.
    if ( !AreTimestampsAtWindow(
            timestamps,
            TTimeIntervalMicroSeconds( KNormalValue ),
            TTimeIntervalMicroSeconds( KMaximum32BitValue ),
            TTimeIntervalMicroSeconds( KNormalValue ),
            TTimeIntervalMicroSeconds( KNormalValue ) ) )
        {
        aResult.SetResult( KErrGeneral, _L("Test case failed. 64 bit max interval failed") );
        }
    timestamps.Reset();

    // If test execution is here, we'll passed
    aResult.SetResult( KErrNone, _L("Test case passed") );

    // Clean up
    timestamps.Close();
    __UHEAP_MARKEND;

    return KErrNone;
    }


// ---------------------------------------------------------------------------
// TEST CASE: Start timer with maximum delay, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithMaximumDelay64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt64 KIllegalValue( 0x7FFFFFFFFFFFFFFF );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   24 (EFlexTimerServerIllegalTimerValue)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 24 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start timer with maximum interval, 64 bit interface
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithMaximumInterval64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback  )
    {
    __UHEAP_MARK;

    const TInt KAllowedValue( 1 );
    const TInt64 KIllegalValue( 0x7FFFFFFFFFFFFFFF );
    
    RArray<TTime> timestamps;
    
    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );

    // ConfigureAndStartTimerL should panic:
    //  Category: "CFlexPeriodic" 
    //  Reason:   24 (EFlexTimerServerIllegalTimerValue)
    // Set the panic code to acceptable exit reason
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::EPanic, 24 );

    ConfigureAndStartTimerL<TTimeIntervalMicroSeconds, TTimeIntervalMicroSeconds>(
        timestamps,
        KAllowedValue,
        KIllegalValue,
        KAllowedValue,
        KAllowedValue );

    // No panic, change result back to normal
    aCallback->TestModuleIf().SetExitReason( CTestModuleIf::ENormal, KErrNone );
    aResult.SetResult( KErrGeneral, _L("Test case failed. No panic.") );
    
    // Clean up
    timestamps.Close();

    __UHEAP_MARKEND;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start timer with NULL callback (32 bit)
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithNullCallBack32L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback )
    {
    return StartWithNullCallBackL<TTimeIntervalMicroSeconds32>( aResult, aCallback );
    }

// ---------------------------------------------------------------------------
// TEST CASE: Start timer with NULL callback (64 bit)
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::StartWithNullCallBack64L(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback )
    {
    return StartWithNullCallBackL<TTimeIntervalMicroSeconds>( aResult, aCallback );
    }

// ---------------------------------------------------------------------------
// TEST CASE: Client crashes
// 1) Start two timers in separate threads
// 2) The 1st timer will panic after a while
// 3) Check that the 2nd timer is ok -> server works OK
// ---------------------------------------------------------------------------
//
TInt CTestFlexPeriodic::ClientCrashL(
    TTestResult& aResult, 
    CTestFlexTimer* aCallback )
    {
    __UHEAP_MARK;

    // Must be bigger than ClientCrashL and StartOneTimerL
    const TTimeIntervalMicroSeconds32 KTestRunTime( 6000000 );

    // Default result if anything leaves i.e. no analyze done.
    aResult.SetResult( KErrGeneral, _L("Test case leaved") );
    
    // Start test case ClientCrashL
    RThread threadA;
    TTestResult resultA;
    TTestCaseArguments caseA = { DoPanicL, resultA, aCallback }; 
    RunInThread( threadA, caseA );

    // Start test case StartOneTimerL
    RThread threadB;
    TTestResult resultB;
    TTestCaseArguments caseB = { StartOneTimerL, resultB, aCallback }; 
    RunInThread( threadB, caseB );

                            //    //  ___     _____
    WaitL( KTestRunTime ); // // // //_ // //  //
                          //_//_// //  // //  //

    // Analyze results
    aResult.SetResult(KErrNone, _L("Test case passed.") );

    // ClientCrashL should be paniced with reason 0xDEAD
    if ( threadA.ExitType() != EExitPanic || threadA.ExitReason() != 0xDEAD )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Client did not panic (correctly).") );
        }
    // StartOneTimerL should be finished ok
    else if ( resultB.iResult != KErrNone || threadB.ExitType() == EExitPanic )
        {
        aResult.SetResult( 
            KErrGeneral, 
            _L("Test case failed. Timer failed.") );
        }

    threadA.Close();
    threadB.Close();
    __UHEAP_MARKEND;

    return KErrNone;
    }