// 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:
//

#include <bautils.h>
#include "TEST.H"

#undef test  //there is a "test" macro which hides "RTest test" declaration.

_LIT(KHelperExeName, "t_LogHiCapHelper.exe");

//======================================================================================================

TPtrC FileName(const TText* aFile)
    {
    TPtrC p(aFile);
    TInt ix=p.LocateReverse('\\');
    if (ix<0)
        ix=p.LocateReverse('/');
    if (ix>=0)
        p.Set(p.Mid(1+ix));
    return p;
    }

//======================================================================================================

void LogTestBoolExpr(TBool aRes, const TText* aFile, TInt aLine)
    {
    if(!aRes)
        {
        TPtrC fname(FileName(aFile));
        RDebug::Print(_L("*** Boolean expression evaluated to false, file: %S-%d\r\n"), &fname, aLine);
        test(EFalse, aLine);
        }
    }

void LogCheck(TInt aValue, TInt aExpected, const TText* aFile, TInt aLine)
	{
	if(aValue != aExpected)
		{
        TPtrC fname(FileName(aFile));
		RDebug::Print(_L("*** Expected error: %d, got: %d, file: %S-%d\r\n"), aExpected, aValue, &fname, aLine);
		test(EFalse, aLine);
		}
	}

void LogCheckU(TUint aValue, TUint aExpected, const TText* aFile, TInt aLine)
	{
	if(aValue != aExpected)
		{
	    TPtrC fname(FileName(aFile));
		RDebug::Print(_L("*** Expected error: %u, got: %u, file: %S-%d\r\n"), aExpected, aValue, &fname, aLine);
		test(EFalse, aLine);
		}
	}

//======================================================================================================

void LogLeave(TInt aErr, const TText* aFile, const TInt aLine)
    {
    TPtrC fname(FileName(aFile));
    RDebug::Print(_L("*** LogEng test leave, err=%d, file: %S-%d\r\n"), aErr, &fname, aLine);
    User::Leave(aErr);
    }

//======================================================================================================

void LogPanic(const TDesC& aCategory, TInt aErr, const TText* aFile, TInt aLine)
    {
    TPtrC fname(FileName(aFile));
    RDebug::Print(_L("*** LogEng test panic'd with err=%d, category=%S, file: %S-%d\r\n"), aErr, &aCategory, &fname, aLine);
    User::Panic(aCategory, aErr);
    }

//======================================================================================================

#ifdef LOGGING_ENABLED

void Log::New()
	{
	_LIT(KNewLogText, "===== NEW LOG =====");
	//
	RFileLogger logger;
	TInt ret=logger.Connect();
	if	(ret==KErrNone)
		{
		logger.CreateLog(KLogFolder, KLogFileName, EFileLoggingModeOverwrite);
		logger.Write(KNewLogText);
		}
	logger.Close();
	}

void Log::Write(const TDesC& aText)
	{
	PruneLogFile();

	RFileLogger logger;
	TInt ret=logger.Connect();
	if (ret==KErrNone)
		{
		logger.SetDateAndTime(EFalse,EFalse);
		logger.CreateLog(KLogFolder, KLogFileName,EFileLoggingModeAppend);
		TBuf<KLogEngLogBufferSize> buf;
		TTime now;
		now.HomeTime();
		TDateTime dateTime;
		dateTime = now.DateTime();
		buf.Format(KTimeFormat,dateTime.Hour(),dateTime.Minute(),dateTime.Second(),dateTime.MicroSecond());
		buf.AppendFormat(KTextFormat,&aText);

		logger.Write(buf);
		}

	logger.Close();
	}

void Log::WriteFormat(TRefByValue<const TDesC> aFmt, ...)
	{
	VA_LIST list;
	VA_START(list,aFmt);

	PruneLogFile();

	TBuf<2*KLogEngLogBufferSize> buf;
	buf.SetMax();
	buf.FillZ();
	TTime now;
	now.HomeTime();
	TDateTime dateTime;
	dateTime = now.DateTime();
	buf.Format(KTimeFormat,dateTime.Hour(),dateTime.Minute(),dateTime.Second(),dateTime.MicroSecond());
	buf.AppendFormatList(aFmt, list );

	RFileLogger logger;
	TInt ret=logger.Connect();
	if (ret==KErrNone)
		{
		logger.SetDateAndTime(EFalse,EFalse);
		logger.CreateLog(KLogFolder, KLogFileName,EFileLoggingModeAppend);
		logger.Write(buf);
		}

	logger.Close();
	}

void Log::PruneLogFile()
  	{
	const TInt KMaxLogSize = 1024 * 500;
	_LIT(KDriveLetter, "C:\\Logs\\");
	//
	TFileName fileName(KDriveLetter);
	fileName.Append(KLogFolder);
	fileName.Append(KLogFileName);
	//
	RFs fsSession;
	if	(fsSession.Connect() == KErrNone)
		{
		TEntry entry;
		if	(fsSession.Entry(fileName, entry) == KErrNone)
			{
			// Check size and delete if its too big
			if	(entry.iSize >= KMaxLogSize)
				fsSession.Delete(fileName); // ignore error
			}
		}
	fsSession.Close();
	}

#endif

// Globals 
GLDEF_D CTrapCleanup* theCleanup;
GLDEF_D CActiveScheduler *testScheduler;
GLDEF_D RFs theFs;
GLDEF_D TFileName theLogName;
GLDEF_D RFile theLog;
GLDEF_D RLogTestSession theLogServ;

//**********************************
// CTestActive
//**********************************

CTestActive::CTestActive(TInt aPriority)
:	CActive(aPriority)
	{
	CActiveScheduler::Add(this);
	iDelayTime=0;
	}

CTestActive::~CTestActive()
	{
	Cancel();
	}

void CTestActive::DoCancel()
	{
	TRequestStatus* s=&iStatus;
	User::RequestComplete(s, KErrNone);
	}

void CTestActive::StartL()
	{
	iDelayCompletion=EFalse;
	iDelayTime=0;
	iStatus = KRequestPending;
	SetActive();
	}

void CTestActive::StartL(TInt aDelay)
	{
	iDelayCompletion=ETrue;
	iDelayTime=aDelay;
	iStatus = KRequestPending;
	SetActive();
	}

void CTestActive::RunL() 
	{
	if(iDelayCompletion && iDelayTime)
		{
		// Wait for events in other threads to have a go....
		User::After(iDelayTime);
		iDelayTime=0;
		iStoredStatus=iStatus;
		SetActive();
		TRequestStatus* s=&iStatus;
		User::RequestComplete(s, KErrNone);
		}
	else
		{
		if(iDelayCompletion)
			iStatus=iStoredStatus;

		LOGTEXT("CTestActive::RunL() - Stopping the scheduler");
		CActiveScheduler::Stop();
		}
	}

//**********************************
// CTestTimer
//**********************************

CTestTimer::CTestTimer()
: CTimer(EPriorityLow)
	{}

void CTestTimer::RunL()
	{
	LOGTEXT("CTestTimer::RunL() - Stopping the scheduler");
	CActiveScheduler::Stop();
	}

CTestTimer* CTestTimer::NewL()
	{
	CTestTimer* self = new(ELeave) CTestTimer();
	CleanupStack::PushL(self);
	self->ConstructL(); // CTimer
	CActiveScheduler::Add(self);
	CleanupStack::Pop();
	return self;
	}

//**********************************
// TestUtils
//**********************************

void TestUtils::Initialize(const TDesC& aName)
	{
    test.Title();
    test.Printf(_L("%S\r\n"), &aName);
    User::RenameThread(aName);
	}

TBool TestUtils::FileExists(const TDesC& aFile)
	{
	TEntry entry;
	return theFs.Entry(aFile, entry) == KErrNone;
	}

//Loads t_loghihelper process and passes for execution to t_loghihelper "aCommandLineArg" command line.
//t_loghihelper will run, execute the command and die, returning the result of the command execution.
//TestUtils::ExecuteRemoteL() will leave if error and return the result of the remote cmd execution to the caller.
TInt TestUtils::ExecuteRemoteL(const TDesC& aCommandLineArg)
	{
	RProcess process;
	LEAVE_IF_ERROR(process.Create(KHelperExeName, aCommandLineArg));
	
	TRequestStatus status;
	process.Logon(status);
	process.Resume();

	User::WaitForRequest(status);
	TInt exitReason = process.ExitReason();
	
	process.Close();
	LEAVE_IF_ERROR(exitReason);

	return exitReason;
	}
	
//Runs t_loghihelper. t_loghihelper will execute the "delete LogEng database" command.
//The "delete LogEng database" is a complex operation. The request is sent via the backup server
//which will send a request to the LogEng server to release the LogEng database file locks and close the file.
//After that the database will be deleted. 
//In the same call the LogEng server will restarted and the LogEng server will re-create the database during the 
//server startup.
//
//If "aCloseBeforeDelete" flag is false, then the database wil be only deleted.
//The default value of "aCloseBeforeDelete" is true: the database will be closed, deleted and re-created.
//But some of the LogEng tests create a CBaBackupSessionWrapper object and call CloseFileL() with the logeng
//database name as a parameter. In this case, if another process, as t_loghicaphelper for example, attempts
//to call CloseFileL() with the same file name as a parameter, then the caller will get KErrServerBusy error.
//See how CBaBackupSessionWrapper::CloseFileL() is implemented on the server side.
void TestUtils::DeleteDatabaseL(TBool aCloseBeforeDelete)
	{
    _LIT(KCmdLine1, "-delete_db1");
    _LIT(KCmdLine2, "-delete_db2");
    (void)ExecuteRemoteL(aCloseBeforeDelete ? KCmdLine1 : KCmdLine2);
	}
	
//Runs t_loghihelper. t_loghihelper will check and return whether the LogEng database is open or not.  
TBool TestUtils::IsDatabaseOpenL()
	{
	_LIT(KCmdLine, "-db_is_open");
	TInt result = ExecuteRemoteL(KCmdLine);
	return result != 0;
	}
	
//Runs t_loghihelper. t_loghihelper will add an event type to the LogEng database.  
void TestUtils::AddEventTypeL()
	{
	_LIT(KCmdLine, "-add_event_type");
	(void)ExecuteRemoteL(KCmdLine);
	}
	
//Runs t_loghihelper. t_loghihelper will add an event to the LogEng database.  
TInt TestUtils::AddEventL()
	{
	_LIT(KCmdLine, "-add_event");
	return ExecuteRemoteL(KCmdLine);		
	}
	
//Runs t_loghihelper. t_loghihelper will add events to the LogEng database.  
void TestUtils::AddViewTestEventsL()
	{
	_LIT(KCmdLine, "-add_view_test_events");
	(void)ExecuteRemoteL(KCmdLine);
	}

//Runs t_loghihelper. t_loghihelper will return the size of the LogEng database.  
TInt TestUtils::DatabaseSizeL()
	{
	_LIT(KCmdLine, "-db_size");
	return ExecuteRemoteL(KCmdLine);		
	}	
	
//Runs t_loghihelper. t_loghihelper will replace the LogEng database with a corrupted database (for testing purposes).
//The LogEng server will be stopped before that. The function can be used only in debug mode.
#ifdef _DEBUG
void TestUtils::CopyCorruptDbL()
	{

	_LIT(KCmdLine, "-copy_corrupt");
	(void)ExecuteRemoteL(KCmdLine);
	}
	
//Runs t_loghihelper. t_loghihelper will replace the LogEng database with a corrupted database (for testing purposes).
//The LogEng server will be stopped before that. The function can be used only in debug mode.
void  TestUtils::CopyCorruptDamagedDbL()
	{

	_LIT(KCmdLine, "-copy_corrupt_damaged");
	(void)ExecuteRemoteL(KCmdLine);
	}
	
//Runs t_loghihelper. t_loghihelper will replace the LogEng database with an old format database 
//(no SimId column, phone number length is different). The LogEng server will be stopped before that.
//The function can be used only in debug mode.
void TestUtils::CopyOldDbL()
	{
	_LIT(KCmdLine, "-copy_old");
	(void)ExecuteRemoteL(KCmdLine);
	}
#else //_DEBUG
void TestUtils::CopyCorruptDbL()
	{
	RDebug::Print(_L("TestUtils::CopyCorruptDbL() has a meaningfull implementation in debug builds only.\n"));
	}

void TestUtils::CopyCorruptDamagedDbL()
	{
	RDebug::Print(_L("TestUtils::CopyCorruptDamagedDbL() has a meaningfull implementation in debug builds only.\n"));
	}

void TestUtils::CopyOldDbL()
	{
	RDebug::Print(_L("TestUtils::CopyOldDbL() has a meaningfull implementation in debug builds only.\n"));
	}

#endif//_DEBUG

//Runs t_loghihelper. t_loghihelper will re-create the LogEng database and check whether LogEng client can connect to the server.   
void TestUtils::TestInvalidSchemaL()
	{
	_LIT(KCmdLine, "-invalid_schema");
	(void)ExecuteRemoteL(KCmdLine);
	}

//Runs t_loghihelper. t_loghihelper checks whether the phone number mathcing is enabled.   
TBool TestUtils::MatchingEnabledL()
	{
	_LIT(KCmdLine, "-is_matching_enabled");
	return ExecuteRemoteL(KCmdLine) != 0;
	}

//Creates HBufC object and puts it on the cleanup stack.
//The buffer will be filled with (' ' + pos) characters, where pos is the character position in the buffer. 
HBufC* TestUtils::CreateBufLC(TInt aLength)
	{
	HBufC* buf = HBufC::NewLC(aLength);
	TPtr ptr = buf->Des();
	for(TInt pos=0;pos<aLength;++pos)
		{
		ptr.Append(TChar(' ' + pos));
		}
	return buf;
	}

//Returns whether the two filters are equal or not.
TBool TestUtils::FiltersEqual(const CLogFilter& aFilter1, const CLogFilter& aFilter2)
	{
	return aFilter1.EventType() == aFilter2.EventType() &&
		   aFilter1.RemoteParty() == aFilter2.RemoteParty() &&
		   aFilter1.Direction() == aFilter2.Direction() &&
		   aFilter1.DurationType() == aFilter2.DurationType() &&
		   aFilter1.Status() == aFilter2.Status() &&
		   aFilter1.Contact() == aFilter2.Contact() &&
		   aFilter1.Number() == aFilter2.Number() 
#ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM	
		   &&
		   aFilter1.SimId() == aFilter2.SimId()
#endif		   
		   ;
	}

//Creates HBufC8 object and puts it on the cleanup stack.
//The buffer will be filled with (' ' + pos % (0xff - 32)) characters, where pos is the character position in the buffer. 
HBufC8* TestUtils::CreateBuf8LC(TInt aLength)
	{
	HBufC8* buf = HBufC8::NewLC(aLength);
	TPtr8 ptr = buf->Des();
	for(TInt pos=0;pos<aLength;++pos)
		{
		ptr.Append(TChar(' ' + pos % (0xff - 32)));
		}
	return buf;
	}

//Returns whether the two events are equal or not.
TBool TestUtils::EventsEqual(const CLogEvent& aEvent1, const CLogEvent& aEvent2)
	{
	return 	aEvent1.Id() == aEvent2.Id() &&
			aEvent1.EventType() == aEvent2.EventType() &&
			aEvent1.RemoteParty() == aEvent2.RemoteParty() &&
			aEvent1.Direction() == aEvent2.Direction() &&
			aEvent1.Time() == aEvent2.Time() &&
			aEvent1.DurationType() == aEvent2.DurationType() &&
			aEvent1.Duration() == aEvent2.Duration() &&
			aEvent1.Status() == aEvent2.Status() &&
			aEvent1.Subject() == aEvent2.Subject() &&
			aEvent1.Number() == aEvent2.Number() &&
			aEvent1.Contact() == aEvent2.Contact() &&
			aEvent1.Link() == aEvent2.Link() &&
			aEvent1.Description() == aEvent2.Description() &&
			aEvent1.Data() == aEvent2.Data() 
#ifdef SYMBIAN_ENABLE_EVENTLOGGER_DUALSIM	
			&& 
			aEvent1.SimId() == aEvent2.SimId()
#endif			
			; 
	}

//Returns whether the two event types are equal or not.
TBool TestUtils::TypesEqual(const CLogEventType& aType1, const CLogEventType& aType2)
	{
	return	aType1.Uid() == aType2.Uid() &&
			aType1.Description() == aType2.Description() &&
			aType1.LoggingEnabled() == aType2.LoggingEnabled();
	}

//Waits for a key to be pressed.
TBool TestUtils::WaitForKeyL(TTimeIntervalMicroSeconds32 aDelay, TKeyCode& aKeyCode)
	{
	TEST(test.Console() != NULL);

	// Create timer
	CTestTimer* timer = CTestTimer::NewL();
	CleanupStack::PushL(timer);
	timer->After(aDelay);

	CTestActive* wait = new(ELeave)CTestActive;
	CleanupStack::PushL(wait);
	wait->StartL();

	// Wait for key press
	test.Console()->Read(wait->iStatus);
	CActiveScheduler::Start();

	// If timer still active a key was pressed
	TBool keyPressed = timer->IsActive();

	if (keyPressed)
		{
		// Get the key pressed
		aKeyCode = test.Console()->KeyCode();

		// Cancel timer
		timer->Cancel();
		}
	else
		{
		// Cancel wait for character
		test.Console()->ReadCancel();
		User::WaitForRequest(wait->iStatus);
		}

	CleanupStack::PopAndDestroy(2); // wait, timer
	return keyPressed;
	}

//Used for LogEng server side heap failure testing.
#ifdef _DEBUG
void TestUtils::SetLogServHeapFailureL(RHeap::TAllocFail aType, TInt aRate)
	{
	//this function doesn't have any effect on UREL builds 
 	//get rid of warnings in release builds
 	aType = aType;
 	aRate = aRate;
	if (!theLogServ.Handle())
	    LEAVE_IF_ERROR(theLogServ.Connect());

	TIpcArgs  ipcArgs(aType,aRate) ;
	LEAVE_IF_ERROR(theLogServ.Send(ELogSetHeapFail, ipcArgs));
	}
#else
void TestUtils::SetLogServHeapFailureL(RHeap::TAllocFail, TInt)
	{
	}
#endif//_DEBUG

//**********************************
// CLogViewChangeObserver
//**********************************

CLogViewChangeObserver* CLogViewChangeObserver::NewLC()
	{
	CLogViewChangeObserver* self = new(ELeave) CLogViewChangeObserver();
	CleanupStack::PushL(self);
	return self;
	}

CLogViewChangeObserver::~CLogViewChangeObserver()
	{
	Cancel();
	delete iChanges;
	}

CLogViewChangeObserver::CLogViewChangeObserver()
:	CActive(EPriorityStandard)
	{
	CActiveScheduler::Add(this);
	}


CLogChangeDefinition* CLogViewChangeObserver::WaitForChangesLC(TStopType aType, TInt aCount)
	{
	__ASSERT_ALWAYS(!iSchedulerStarted, User::Invariant());
	Reset();
	//
	iExpectedChangeCount = aCount;
	iType = aType;
	if	(aType != EStopOnChanges)
		SetActive();
	//
	iSchedulerStarted = ETrue;
	CActiveScheduler::Start();
	iSchedulerStarted = EFalse;
	//
	CLogChangeDefinition* ret = iChanges;
	TEST(iChanges != NULL);
	iChanges = NULL;
	CleanupStack::PushL(ret);
	return ret;
	}

CLogChangeDefinition* CLogViewChangeObserver::WaitForChangesLC(TCallBack aCallBack, TStopType aType, TInt aCount)
	{
	iHaveCallBack = ETrue;
	iCallBack = aCallBack;
	return WaitForChangesLC(aType, aCount);
	}

void CLogViewChangeObserver::HandleLogViewChangeEventAddedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
	{
	AddChangeL(ELogChangeTypeEventAdded, aId, aViewIndex);
	if	(aChangeIndex == aTotalChangeCount-1)
		CheckForSchedulerStop();
	}

void CLogViewChangeObserver::HandleLogViewChangeEventChangedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
	{
	AddChangeL(ELogChangeTypeEventChanged, aId, aViewIndex);
	if	(aChangeIndex == aTotalChangeCount-1)
		CheckForSchedulerStop();
	}

void CLogViewChangeObserver::HandleLogViewChangeEventDeletedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
	{
	AddChangeL(ELogChangeTypeEventDeleted, aId, aViewIndex);
	if	(aChangeIndex == aTotalChangeCount-1)
		CheckForSchedulerStop();
	}

void CLogViewChangeObserver::RunL()
	{
	__ASSERT_ALWAYS(iType == EStopOnRunL || iType == EStopOnBoth, User::Invariant());
	iHaveFinishedOperation = ETrue;
	CheckForSchedulerStop();
	}

void CLogViewChangeObserver::DoCancel()
	{
	TRequestStatus* s=&iStatus;
	User::RequestComplete(s, KErrCancel);
	}

void CLogViewChangeObserver::Reset()
	{
	iExpectedChangeCount = 0;
	iHaveFinishedOperation = EFalse;
	iHaveObtainedChanges = EFalse;
	iSchedulerStarted = EFalse;
	iType = EStopOnChanges;
	delete iChanges;
	iChanges = NULL;
	}

void CLogViewChangeObserver::CheckForSchedulerStop()
	{
	if(iSchedulerStarted)
		{
		if	(iHaveCallBack)
			{
			iCallBack.CallBack();
			iCallBack.iFunction = NULL;
			iCallBack.iPtr = NULL;
			iHaveCallBack = EFalse;
			}
		//
		TBool stopScheduler = EFalse;
		switch(iType)
			{
		case EStopOnChanges:
			stopScheduler = iHaveObtainedChanges;
			break;
		case EStopOnRunL:
			stopScheduler = iHaveFinishedOperation;
			break;
		case EStopOnBoth:
			stopScheduler = (iHaveObtainedChanges && iHaveFinishedOperation);
			break;
		case EStopOnCount:
			if	(iChanges)
				{
				TEST(iChanges->Count() <= iExpectedChangeCount);
				stopScheduler = (iChanges->Count() == iExpectedChangeCount);
				}
		case EDontStopScheduler:
			break;
			}

		if	(stopScheduler)
			{
			LOGTEXT("CLogViewChangeObserver::CheckForSchedulerStop() - Stopping the scheduler");
			CActiveScheduler::Stop();
			}
		}
	}

void CLogViewChangeObserver::AddChangeL(TLogDatabaseChangeType aType, TLogId aId, TInt aViewIndex)
	{
	CLogChangeDefinition* changes;

	if	(iChanges)
		changes = iChanges;
	else
		{
		changes = CLogChangeDefinition::NewL();
		CleanupStack::PushL(changes);
		}
	//
	changes->AddL(aId, aType, aViewIndex);
	//
	if	(!iChanges)
		{
		delete iChanges;
		iChanges = changes;
		CleanupStack::Pop(changes);
		}
	//
	iHaveObtainedChanges = ETrue;
	}

//**********************************
// CLogViewChangeObserverErrorTest
//**********************************
CLogViewChangeObserverErrorTest* CLogViewChangeObserverErrorTest::NewLC()
	{
	CLogViewChangeObserverErrorTest* self = new(ELeave) CLogViewChangeObserverErrorTest();
	CleanupStack::PushL(self);
	return self;
	}	

CLogViewChangeObserverErrorTest::CLogViewChangeObserverErrorTest()
	{}
	 
void CLogViewChangeObserverErrorTest::HandleLogViewChangeEventAddedL(TLogId aId, TInt aViewIndex, TInt aChangeIndex, TInt aTotalChangeCount)
	{
  	// DEF108741L - the error condition tested here is that a leave is dealt with 
  	// gracefully without any panics.
 
 	// Add a new event to the log
	AddChangeL(ELogChangeTypeEventAdded, aId, aViewIndex);
	if	(aChangeIndex == aTotalChangeCount-1)
		CheckForSchedulerStop();
	
	// In the test case for DEF108741L this method will be effectively
	// invoked 3 times. This code forces a leave on the middle event to 
	// ensure that the leave is dealt with and the rest of the test 
	// completes successfully.
	if (aId == 1)
		{	
		LEAVE(KErrGeneral);
		} 
	}
 
//**********************************
// CLogSchedulerTimer
//**********************************

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

CLogSchedulerTimer::~CLogSchedulerTimer()
	{
	Cancel();
	}

CLogSchedulerTimer::CLogSchedulerTimer()
:	CTimer(0)
	{
	CActiveScheduler::Add(this);
	}

void CLogSchedulerTimer::ConstructL()
	{
	CTimer::ConstructL();
	}

void CLogSchedulerTimer::Wait(TTimeIntervalMicroSeconds32 aTime)
	{
	After(aTime);
	CActiveScheduler::Start();
	}

void CLogSchedulerTimer::RunL()
	{
	LOGTEXT("CLogSchedulerTimer::RunL() - Stopping the scheduler");
	CActiveScheduler::Stop();
	}




//**********************************
// CLogChangeNotifier
//**********************************

CLogChangeNotifier* CLogChangeNotifier::NewL()
	{
	CLogChangeNotifier* self = new(ELeave)CLogChangeNotifier();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CLogChangeNotifier::~CLogChangeNotifier()
	{
	Cancel();
	delete iClient;
	}

CLogChangeNotifier::CLogChangeNotifier()
: CActive(EPriorityStandard)
	{
	CActiveScheduler::Add(this);
	}

void CLogChangeNotifier::ConstructL()
	{
	iClient = CLogClient::NewL(theFs);

	iStart.UniversalTime();
	iClient->NotifyChange(10000000, iStatus);
	SetActive();
	}

void CLogChangeNotifier::RunL()
	{
	TTime now;
	now.UniversalTime();
	TTimeIntervalSeconds seconds;
	now.SecondsFrom(iStart, seconds);

	TBuf<256> buf;
 	const TInt error = iStatus.Int();
 	if (error == KErrServerTerminated)
 		{
 		buf.Format(_L("KErrServerTerminated"));
		User::InfoPrint(buf);
		return;
 		}
 		
	buf.Format(_L("%d seconds"), seconds.Int());
	User::InfoPrint(buf);
	
	iStart.UniversalTime();
	iClient->NotifyChange(10000000, iStatus);
	SetActive();
	}

void CLogChangeNotifier::DoCancel()
	{
	iClient->NotifyChangeCancel();	
	}

//**********************************
// Global
//**********************************

void SetupSchedulerL()
	{
	testScheduler = new (ELeave) CActiveScheduler;
	CleanupStack::PushL( testScheduler );
	CActiveScheduler::Install( testScheduler );
	}

void CloseScheduler()
	{
    CleanupStack::PopAndDestroy(); // Scheduler
    testScheduler = NULL;
	}

static void CreateLogL()
    {
    LEAVE_IF_ERROR(theFs.Connect());

    theLogName.Copy(RProcess().FileName());
    TInt start = theLogName.LocateReverse('\\');
    TInt end = theLogName.LocateReverse('.');
    theLogName = theLogName.Mid(start + 1, end - start - 1);

    // create the log filename
    theLogName.Insert(0, _L("C:\\"));
#if defined(__WINS__)
    theLogName.Append(_L(".WINS."));
#else
    theLogName.Append(_L(".MARM."));
#endif
#if defined(_UNICODE)
    theLogName.Append(_L("UNICODE."));
#else
    theLogName.Append(_L("ASCII."));
#endif
#if defined(_DEBUG)
    theLogName.Append(_L("DEB."));
#else
    theLogName.Append(_L("REL."));
#endif
    theLogName.Append(_L("LOG"));

    // create the logfile
    LEAVE_IF_ERROR(theLog.Replace(theFs, theLogName, EFileWrite|EFileShareExclusive));
    TBuf8<256> text;
    text.Copy(theLogName);
    theLog.Write(text);
    theLog.Write(_L8("\nTest results\n"));
    }

static void CloseLog()
    {
    theLog.Write(_L8("Tests completed\n"));
    test.Printf(_L("Results saved in %S\n"), &theLogName);
    theLog.Close();
    theFs.Close();
    }

void DeleteDataFile(const TDesC& aFullName)
	{
	RFs fsSession;
	TInt err = fsSession.Connect();
	if(err == KErrNone)
		{
		TEntry entry;
		if(fsSession.Entry(aFullName, entry) == KErrNone)
			{
			RDebug::Print(_L("Deleting \"%S\" file.\n"), &aFullName);
			err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName);
				}
			err = fsSession.Delete(aFullName);
			if(err != KErrNone) 
				{
				RDebug::Print(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName);
				}
			}
		fsSession.Close();
		}
	else
		{
		RDebug::Print(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName);
		}
	}

static void Cleanup(void*)
	{
	TRAP_IGNORE(TestUtils::DeleteDatabaseL());
	_LIT(KCntModelFileName, "c:\\system\\data\\CntModel.ini");
	::DeleteDataFile(KCntModelFileName);
	_LIT(KContactsFileName, "c:\\system\\data\\Contacts.cdb");
	::DeleteDataFile(KContactsFileName);
	::DeleteDataFile(theLogName);
	}

static void DoMainL()
	{
	::SetupSchedulerL();
	TCleanupItem cleanup(&Cleanup, NULL);
	CleanupStack::PushL(cleanup);
	CreateLogL();
	::doTestsL();
	CloseLog();
    CleanupStack::PopAndDestroy();//cleanup
	::CloseScheduler();
	}

TInt E32Main()
	{	
	__UHEAP_MARK;

	theCleanup = CTrapCleanup::New();
    if(!theCleanup)
       {
       _LIT(KLogHiCapHelperPanic, "LogTestPanic");
        User::Panic(KLogHiCapHelperPanic, KErrNoMemory);
       }

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

	delete theCleanup;	

	test.Console()->SetPos(0, 13);

	test.End();
	test.Close();

	__UHEAP_MARKEND;

	return KErrNone;
	}


