// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
//
// Description:
// This test will be able to do what it is planned to do only if the shutdown client and server
// components are built with SYSLIBS_TEST macro defined.
// 
//

#include <e32test.h>        //RTest
#include <e32svr.h>         //RDebug
#include <savenotf.h>       //RSaveSession, MSaveObserver, CSaveNotifier

static RTest TheTest(_L("T_PwrBasic"));
const TInt KOneSec = 1000000;
_LIT(KTestFailed, "Invalid Test Step %d\n");

// The timeout of 2 seconds which is longer than the one defined at server side(1.5 seconds).
// It triggers events after CServShutdownServer::SwitchOff() has been called.
const TUint32 KShtdwnTimeoutLonger =2000000;

// The timeout of 1 second which is shorter than the one defined at server side(1.5 seconds).
// It triggers events before CServShutdownServer::SwitchOff() has been called.
const TUint32 KShtdwnTimeoutShorter =1000000;

//
//
//Test macroses and functions
static void Check(TInt aValue, TInt aLine)
	{
	if(!aValue)
		{
		TheTest(EFalse, aLine);
		}
	}
static void Check(TInt aValue, TInt aExpected, TInt aLine)
	{
	if(aValue != aExpected)
		{
		RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue);
		TheTest(EFalse, aLine);
		}
	}
#define TEST(arg) ::Check((arg), __LINE__)
#define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__)

//

static void Leave(TInt aLine, TInt aError)
	{
	RDebug::Print(_L("*** Leave. Error: %d, line: %d\r\n"), aError, aLine);
	User::Leave(aError);
	}
static void LeaveIfError(TInt aLine, TInt aError)
	{
	if(aError < KErrNone)
		{
		::Leave(aLine, aError);
		}
	}
static void LeaveIfNull(TInt aLine, TAny* aPtr)
	{
	if(!aPtr)
		{
		::Leave(aLine, KErrNoMemory);
		}
	}
#define __LEAVE(err) ::Leave(__LINE__, err)
#define __LEAVE_IF_ERROR(err) ::LeaveIfError(__LINE__, err)
#define __LEAVE_IF_NULL(ptr) ::LeaveIfNull(__LINE__, ptr)

//
//
//Test classes & objects

//This test class is used to receive a powerdown notification from the shutdown server.
class CPowerdownClient : public CActive, public MSaveObserver
    {
public:
    static CPowerdownClient* NewLC();
    virtual ~CPowerdownClient();
    virtual void SaveL(MSaveObserver::TSaveType aSaveType);

protected:
	virtual void DoCancel();
	virtual void RunL();

private:
    CPowerdownClient();
    void ConstructL();

private:
    RTimer iTimer;
    CSaveNotifier* iSaveNotifier;

    };

CPowerdownClient* CPowerdownClient::NewLC()
    {
    CPowerdownClient* self = new CPowerdownClient;
    __LEAVE_IF_NULL(self);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CPowerdownClient::~CPowerdownClient()
    {
    Cancel();
    iTimer.Close();
    delete iSaveNotifier;
    }

//MSaveNotifier::SaveL() implementation. Called when powerdown event occurs.
void CPowerdownClient::SaveL(MSaveObserver::TSaveType)
    {
    iSaveNotifier->DelayRequeue();

    iTimer.After(iStatus, KOneSec);
    TEST2(iStatus.Int(), KRequestPending);
    SetActive();
    }

void CPowerdownClient::DoCancel()
    {
    iTimer.Cancel();
    }

//Processes timer events.
void CPowerdownClient::RunL()
    {
    static TBool powerOffCalled = EFalse;
    if(!powerOffCalled)
        {
        TEST2(iSaveNotifier->SwitchOff(MSaveObserver::ESaveAll, ETrue), KErrNone);
        powerOffCalled = ETrue;
        }
    else
        {
        iSaveNotifier->HandleError(KErrGeneral);
        User::After(KOneSec);
        CActiveScheduler::Stop();
        }
    }

//Adds the CPowerdownClient object to the active scheduler.
CPowerdownClient::CPowerdownClient() :
    CActive(CActive::EPriorityStandard)
    {
	CActiveScheduler::Add(this);
    }

//Constructs CPowerdownClient object and generates a timer event, processed 1 second later.
void CPowerdownClient::ConstructL()
    {
    iSaveNotifier = CSaveNotifier::NewL(*this);
    __LEAVE_IF_ERROR(iTimer.CreateLocal());
    iTimer.After(iStatus, KOneSec);
    TEST2(iStatus.Int(), KRequestPending);
    SetActive();
    }
/**
  DEF048920 - PwrCli server may panic if CSaveNotifier::HandleError() is called from...
  What the test does is:
  1) The test creates a CPowerdownClient object.
  2) CPowerdownClient object will raise a timer event, which will be processed after 1 second.
  3) The test starts the scheduler.
  4) The timer event gets processed in CPowerdownClient::RunL() - it initiates a powerdown
     sequence.
  5) The shutdown server will notify the client (CPowerdownClient), calling 
     CPowerdownClient::SaveL() method.
  6) CPowerdownClient::SaveL() will call CSaveNotifier::DelayRequeue() not to re-register itself. 
     Then it will raise a timer event, which will be processed after 1 second.
  7) The second timer event gets processed in CPowerdownClient::RunL() - it calls 
     CSaveNotifier::HandleError(). 
     Without the fix CSaveNotifier::HandleError() causes "BadHandle" panic on the server side.

@SYMTestCaseID          SYSLIB-PWRLCLI-CT-3686
@SYMTestCaseDesc	    Tests for defect number DEF048920
@SYMTestPriority 	    High
@SYMTestActions  	    Initiate a powerdown sequence.The shutdown server will notify the client,which delays the 
                        power off and then raises a timer event which stops the scheduler   
@SYMTestExpectedResults Test must not fail
@SYMREQ                 REQ0000
*/		
void DEF048920L()
    {	
	CActiveScheduler* scheduler = new CActiveScheduler;
    __LEAVE_IF_NULL(scheduler);
    CleanupStack::PushL(scheduler);
	CActiveScheduler::Install(scheduler);

    CPowerdownClient* powerdownClient = CPowerdownClient::NewLC();

	CActiveScheduler::Start();

    CleanupStack::PopAndDestroy(powerdownClient);
    CleanupStack::PopAndDestroy(scheduler);
    }



// This test class is used to receive a powerdown notification from the shutdown server.
// Its SaveL() calls MSaveObserver::DelayRequeue() not to re-register itself. Its RunL() may call 
// CSaveNotifier::HandleError() to send error to the server. But both behaviors will not stop the server's 
// shutdown execution.
class CPowerdownClient2 : public CActive, public MSaveObserver
    {
public:
    static CPowerdownClient2* NewLC();
    virtual ~CPowerdownClient2();
    virtual void SaveL(MSaveObserver::TSaveType aSaveType);

protected:
	virtual void DoCancel();
	virtual void RunL();

private:
    CPowerdownClient2();
    void ConstructL();

private:
	enum TTestSteps
		{
		TRequestSwitchOff1,
		TClientSave1,
		TClientCheckServerPowerOff1,		
		TRequestSwitchOff2,
		TClientSave2,
		TClientSendError,
		TClientCheckServerPowerOff2,
		TTestStepEnd,
		};

private:
    RTimer iTimer;
    CSaveNotifier* iSaveNotifier;
    TTestSteps iTestStep;
    };

CPowerdownClient2* CPowerdownClient2::NewLC()
    {
    CPowerdownClient2* self = new CPowerdownClient2();
    __LEAVE_IF_NULL(self);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CPowerdownClient2::~CPowerdownClient2()
    {
    Cancel();
    iTimer.Close();
    delete iSaveNotifier;
    }

//MSaveNotifier::SaveL() implementation. Called when powerdown event occurs.
void CPowerdownClient2::SaveL(MSaveObserver::TSaveType)
    {
	__ASSERT_ALWAYS(iTestStep == TClientSave1 || iTestStep == TClientSave2, \
		TheTest.Panic(KErrNotSupported, KTestFailed, iTestStep));
	
	TBool powerOff = EFalse;
	
	// Checks the power state of the server. It should be off because the server has received
	// the power off request.
	// Calls DelayRequeue() not to re-register itself.
	// Starts a timer which is longer than the server's shutdown timer.
	// Move to next test step
	if(iTestStep == TClientSave1)
		{
		iSaveNotifier->ServerPowerState(powerOff);
		TEST(powerOff);		
		iSaveNotifier->DelayRequeue();
   		iTestStep = TClientCheckServerPowerOff1;
   		iTimer.After(iStatus, KShtdwnTimeoutLonger);
   		TEST2(iStatus.Int(), KRequestPending);
   		SetActive();
		}

	// Checks the power state of the server. It should be off because the server has received
	// the power off request.
	// Calls DelayRequeue() not to re-register itself.
	// Starts a timer which is shorter than the server's shutdown timer.
	// Move to next test step
   	if(iTestStep == TClientSave2)
   		{
		iSaveNotifier->ServerPowerState(powerOff);
		TEST(powerOff);		
		iSaveNotifier->DelayRequeue();
		iTestStep = TClientSendError;
   		iTimer.After(iStatus, KShtdwnTimeoutShorter);
   		TEST2(iStatus.Int(), KRequestPending);
   		SetActive();
   		}
    }

void CPowerdownClient2::DoCancel()
    {
    iTimer.Cancel();
    }

//Processes timer events.
void CPowerdownClient2::RunL()
    {
	__ASSERT_ALWAYS(iTestStep == TRequestSwitchOff1 || iTestStep == TClientCheckServerPowerOff1 ||\
				 iTestStep == TClientSendError, TheTest.Panic(KErrNotSupported, KTestFailed, iTestStep));    
	
	TBool powerOff = ETrue;
	
	// Requests server for switchoff for the first time
    if(iTestStep == TRequestSwitchOff1)
    	{
    	TEST2(iSaveNotifier->SwitchOff(MSaveObserver::ESaveAll, ETrue), KErrNone);
    	iTestStep = TClientSave1;
    	}
    
	// Checks the power state of the server. It should be on because CServShutdownServer::SwitchOff()
	// has been called.
    // Requests server for switchoff for the second time.
    if(iTestStep == TClientCheckServerPowerOff1)
    	{
		iSaveNotifier->ServerPowerState(powerOff);
		TEST(!powerOff);
    	iSaveNotifier->Queue(); // client re-register after the system resumes.
    	iTestStep = TRequestSwitchOff2;
    	User::After(KOneSec);
    	iTestStep = TClientSave2;
    	TEST2(iSaveNotifier->SwitchOff(MSaveObserver::ESaveAll, ETrue), KErrNone);
    	}
    	
	// Checks the power state of the server. It should be off because CServShutdownServer::SwitchOff()
	// has not been called.
	// Sends error to server. The server will ignore the error message. 
	// HandleError re-registers the client thus triggers the CServShutdownServer::SwitchOff().
	// Checks the power state of the server again. It should be on because CServShutdownServer::SwitchOff()
	// has been called.
	// Stops the active scheduler.
    if(iTestStep == TClientSendError)
    	{
		iSaveNotifier->ServerPowerState(powerOff);
		TEST(powerOff);	
    	iSaveNotifier->HandleError(KErrGeneral);
    	iTestStep = TClientCheckServerPowerOff2;
    	User::After(KOneSec);
		iSaveNotifier->ServerPowerState(powerOff);
		TEST(!powerOff);		
		iTestStep = TTestStepEnd;
		CActiveScheduler::Stop();
    	}
    }

//Adds the CPowerdownClient2 object to the active scheduler.
CPowerdownClient2::CPowerdownClient2() :
    CActive(CActive::EPriorityStandard), iTestStep(TRequestSwitchOff1)
    {
	CActiveScheduler::Add(this);
    }

//Constructs CPowerdownClient2 object and generates a timer event, processed 1 second later.
void CPowerdownClient2::ConstructL()
    {
    iSaveNotifier = CSaveNotifier::NewL(*this);
    __LEAVE_IF_ERROR(iTimer.CreateLocal());
    iTimer.After(iStatus, KOneSec);
    TEST2(iStatus.Int(), KRequestPending);
    SetActive();
    }


/**
  DEF111025 - Device restarts from PREQ1089 UIF functionality can be delayed indefinitely.
  What this test does is:
  1) The test creates a CPowerdownClient2 object, which raises a 1-second timer event. The test starts 
     the scheduler.
  2) The timer event gets processed in CPowerdownClient2::RunL() - it initiates a powerdown
     sequence.
  3) The shutdown server notifies the client (CPowerdownClient2), calling CPowerdownClient2::SaveL() 
     method.
  4) CPowerdownClient2::SaveL() gets the power state of the server and checks whether it is off to ensure 
     the server has received the shutdown request but not executed CServShutdownServer::SwitchOff() yet. 
     It calls CSaveNotifier::DelayRequeue() not to re-register itself. 
     Then it raises a timer, which is longer than the shutdown timer defined at the server side.
  5) The timer event gets processed in CPowerdownClient::RunL(). It gets the power state of the server 
     and checks whether it is on to ensure the server has had CServShutdownServer::SwitchOff() executed by 
     the shutdown timer. 
     Then it initiates a powerdown sequence for the second time.
  6) The shutdown server notifies the client (CPowerdownClient2), calling  CPowerdownClient2::SaveL() 
     method.
  7) CPowerdownClient2::SaveL() gets the power state of the server and checks whether it is off to ensure 
     the server has received the shutdown request but not executed CServShutdownServer::SwitchOff() yet. 
     It calls CSaveNotifier::DelayRequeue() not to re-register itself. 
     Then it raises a timer, which is shorter than the shutdown timer defined at the server side.
  8) The timer event gets processed in CPowerdownClient::RunL(). It gets the power state of the server 
     and checks whether it is off to ensure the server still has not executed CServShutdownServer::SwitchOff(). 
     It calls CSaveNotifier::HandleError() to send an error to the server. After sometime, It gets the power 
     state of the server again and checks whether it is on to ensure the server has had 
     CServShutdownServer::SwitchOff() executed by the shutdown timer.
     Then it stops the active scheduler.
     
  Without the fix the checking of power state of the server would fail in a certain step.

@SYMTestCaseID          SYSLIB-PWRCLI-CT-4001
@SYMTestCaseDesc	    Tests for defect number DEF111025
@SYMTestPriority 	    High
@SYMTestActions  	    Initiates a powerdown sequence which sends 2 poweroff requests to server. The shutdown 
					    server will notify the client twice. The first time when being notified, the client will 
					    not re-register itself. For the second time, it will send error to the server. During 
					    this procedure, some checkpoints will be set to check the power states of the server.
@SYMTestExpectedResults The test program should not panic or leave. At power state checkpoints, the power states 
						queried from the server should be the expected values.
@SYMDEF                 DEF111025
*/		
void DEF111025L()
	{	
	CActiveScheduler* scheduler = new CActiveScheduler;
    __LEAVE_IF_NULL(scheduler);
    CleanupStack::PushL(scheduler);
	CActiveScheduler::Install(scheduler);
	
    CPowerdownClient2* powerdownClientTimer = CPowerdownClient2::NewLC();

	CActiveScheduler::Start();
	CleanupStack::PopAndDestroy(powerdownClientTimer);
    CleanupStack::PopAndDestroy(scheduler);
	}
//
//
//Tests

static void DoRunL()
    {
	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-PWRLCLI-CT-3686  DEF048920 "));
    ::DEF048920L(); 
    TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-PWRCLI-CT-4001 DEF111025 "));
    ::DEF111025L();
    }

TInt E32Main()
	{
	__UHEAP_MARK;
	CTrapCleanup* tc = CTrapCleanup::New();
    TEST(tc != NULL);

	TheTest.Start(_L("Power shutdown tests"));

    TRAPD(err, ::DoRunL());
	TEST2(err, KErrNone);

	TheTest.End();
	TheTest.Close();
	
	delete tc;

	__UHEAP_MARKEND;

	User::Heap().Check();
	return KErrNone;
	}
