diff -r 000000000000 -r bde4ae8d615e os/persistentdata/loggingservices/eventlogger/test/src/t_logutil2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/persistentdata/loggingservices/eventlogger/test/src/t_logutil2.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,936 @@ +// Copyright (c) 2004-2010 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 +#include "t_logutil2.h" + +//Define "TheTest" variable used in the test cpp files +extern RTest TheTest; + +_LIT(KHelperExeName, "t_LogHiCapHelper.exe"); + +//====================================================================================================== + +#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 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 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) + { + TheTest.Title(); + TheTest.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() + { + TheTest.Printf(_L("TestUtils::CopyCorruptDbL() has a meaningfull implementation in debug builds only.\n")); + } + +void TestUtils::CopyCorruptDamagedDbL() + { + TheTest.Printf(_L("TestUtils::CopyCorruptDamagedDbL() has a meaningfull implementation in debug builds only.\n")); + } + +void TestUtils::CopyOldDbL() + { + TheTest.Printf(_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;posDes(); + for(TInt pos=0;posAfter(aDelay); + + CTestActive* wait = new(ELeave)CTestActive; + CleanupStack::PushL(wait); + wait->StartL(); + + // Wait for key press + TheTest.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 = TheTest.Console()->KeyCode(); + + // Cancel timer + timer->Cancel(); + } + else + { + // Cancel wait for character + TheTest.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")); + TheTest.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) + { + TheTest.Printf(_L("Deleting \"%S\" file.\n"), &aFullName); + err = fsSession.SetAtt(aFullName, 0, KEntryAttReadOnly); + if(err != KErrNone) + { + TheTest.Printf(_L("Error %d changing \"%S\" file attributes.\n"), err, &aFullName); + } + err = fsSession.Delete(aFullName); + if(err != KErrNone) + { + TheTest.Printf(_L("Error %d deleting \"%S\" file.\n"), err, &aFullName); + } + } + fsSession.Close(); + } + else + { + TheTest.Printf(_L("Error %d connecting file session. File: %S.\n"), err, &aFullName); + } + } + +static void Cleanup(void*) + { + TRAP_IGNORE(TestUtils::DeleteDatabaseL()); + ::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; + + TheTest.Console()->SetPos(0, 13); + + TheTest.End(); + TheTest.Close(); + + __UHEAP_MARKEND; + + return KErrNone; + } + +