diff -r 6edeef394eb7 -r 9397a16b6eb8 testexecmdw/tef/tef/utils/src/testserverbase.cpp --- a/testexecmdw/tef/tef/utils/src/testserverbase.cpp Fri Sep 03 07:55:01 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1482 +0,0 @@ -/* -* Copyright (c) 2005-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: -* Implementation of the following classes -* CTestServer -* n x CTestSession Maps to RTestServ session) -* n x CStepControl or CPersistentStepControl (Maps to RTestSession) -* CTestStep (1 to 1 mapping with parent) -* CTestServer -* Derives CServer -* Test servers derive from this and implement the CreateTestStep() pure virtual -* CTestSession -* Derives CSharableSession -* Implements the ServiceL() pure virtual and creates CStepControl object(s) -* when a test step is opened. -* Implements the MSessionNotify interface for callbacks from CStepControl when -* a test step completes. -* CStepControl -* Derives CActive -* Runs a test step instance in its own thread and heap -* CTestStep -* Derives CBase -* Test Servers derive test steps from this -* -*/ - - - -/** - @file TestClient.cpp -*/ - -#include "testexecuteserverbase.h" -#include "testserver2.h" -#include "testexecuteserverutils.h" -#include "testexecuteclient.h" -#include -#include -#include - -const TUint KDefaultHeapSize = 0x100000; - -//> @internalComponent -// see the impletation below. -void SytemWideErrToTefErr(TInt &aErr); -/** - * Constructor - */ -EXPORT_C CTestServer::CTestServer() : CServer2(EPriorityStandard) -, iSeed(0) -, iSessionCount(0) - { - // Random seed for unique thread id's - iSeed = (TInt)this; - // Default is not to allow Server Logging - iLoggerStarted = EFalse; - } - -/** - * Destructor - */ -EXPORT_C CTestServer::~CTestServer() - { - if (iLoggerStarted) - { - // Shut down the Servers' logger instance. - Logger().Close(); - } - } - -/** - * @param aName - Reference to the Server name - * StartL + initiate server logging - * Servers can StartL themselves or call this to gain server logging. - */ -EXPORT_C void CTestServer::ConstructL(const TDesC& aName) - { - StartL(aName); - StartLoggerL(); - iServerName = aName; - } - -/** - * Will extract the script logfile name from the temporary file 'LogFileName.txt' - * (assuming no ScheduleTest compatible logging) and then opens a logging session - * to that file. If ScheduleTest logging is in effect then we will open ScriptEngine.htm - * instead as the file to log to. - */ -void CTestServer::StartLoggerL() - { - TDriveName defaultSysDrive(KTEFLegacySysDrive); - RFs fileServer; - TVersionName version(fileServer.Version().Name()); - - RLibrary pluginLibrary; - CWrapperUtilsPlugin* plugin = TEFUtils::WrapperPluginNew(pluginLibrary); - - if (plugin!=NULL) - { - TDriveUnit driveUnit(plugin->GetSystemDrive()); - defaultSysDrive.Copy(driveUnit.Name()); - delete plugin; - pluginLibrary.Close(); - } - - CTestExecuteIniData* parseTestExecuteIni = NULL; - TBuf resultFilePath; - TBuf xmlFilePath; - TInt logMode; - TInt logLevel; - - TRAPD(err,parseTestExecuteIni = CTestExecuteIniData::NewL(defaultSysDrive)); - if (err == KErrNone) - { - CleanupStack::PushL(parseTestExecuteIni); - parseTestExecuteIni->ExtractValuesFromIni(); - parseTestExecuteIni->GetKeyValueFromIni(KTEFHtmlKey, resultFilePath); - parseTestExecuteIni->GetKeyValueFromIni(KTEFXmlKey, xmlFilePath); - parseTestExecuteIni->GetKeyValueFromIni(KTEFLogMode, logMode); - parseTestExecuteIni->GetKeyValueFromIni(KTEFLogSeverityKey, logLevel); - } - else - { - TBuf testExecuteLogPath(KTestExecuteLogPath); - testExecuteLogPath.Replace(0, 2, defaultSysDrive); - resultFilePath.Copy(testExecuteLogPath); - xmlFilePath.Copy(testExecuteLogPath); - logMode = TLoggerOptions(ELogHTMLOnly); - logLevel = RFileFlogger::TLogSeverity(ESevrAll); - } - Logger().SetLoggerOptions(logMode); - - // Initialise a handle to the file logger - User::LeaveIfError(Logger().Connect()); - RFs fS; - User::LeaveIfError(fS.Connect()); - CleanupClosePushL(fS); - RFile file; - TBuf xmlLogFile(xmlFilePath); - TBuf logFile; - TBuf logFileNameFile(resultFilePath); - logFileNameFile.Append(KTestExecuteScheduleTestLogCompatibilityNameFile); - if(file.Open(fS,logFileNameFile,EFileRead | EFileShareAny) != KErrNone) - { - // If LogFileName.txt is not present then we are using ScheduleTest - // compliant logging, any logging issued by the server will therefore - // go to ScriptEngine.htm - _LIT(KScriptEngine,"ScriptEngine"); - logFile.Copy(KScriptEngine); - } - else - { - CleanupClosePushL(file); - TBuf8 logFile8; - TInt fileSize; - User::LeaveIfError(file.Size(fileSize)); - User::LeaveIfError(file.Read(logFile8,fileSize)); - logFile.Copy(logFile8); - CleanupStack::Pop(&file); - file.Close(); - } - - xmlLogFile.Append(logFile); - _LIT(KXmlExtension,".xml"); - xmlLogFile.Append(KXmlExtension); - _LIT(KHtmExtension,".htm"); - logFile.Append(KHtmExtension); - TBuf logFilePath(resultFilePath); - logFilePath.Append(logFile); - CleanupStack::Pop(&fS); - fS.Close(); - - if (logMode == 0 || logMode == 2) - { - User::LeaveIfError(Logger().HtmlLogger().CreateLog(logFilePath,RTestExecuteLogServ::ELogModeAppend)); - Logger().HtmlLogger().SetLogLevel(TLogSeverity(logLevel)); - } - - if (logMode == 1 || logMode == 2) - { - User::LeaveIfError(Logger().XmlLogger().CreateLog(xmlLogFile,RFileFlogger::ELogModeAppend)); - Logger().XmlLogger().SetLogLevel(RFileFlogger::TLogSeverity(logLevel)); - } - - User::LeaveIfError( Logger().ShareAuto() ); - iLoggerStarted = ETrue; - if (parseTestExecuteIni != NULL) - { - CleanupStack::PopAndDestroy(parseTestExecuteIni); - } - } - -/** - * Last one out switch off the lights - * Stop the active sheduler and hence the server, if this is the last session - */ -void CTestServer::SessionClosed() - { - iSessionCount--; - if (iSessionCount == 0) - CActiveScheduler::Stop(); - } - -/** - * @param RMessage - RMessage for the session open - * Secure version - */ -EXPORT_C CSession2* CTestServer::NewSessionL(const TVersion& /*aVersion*/,const RMessage2& /*aMessage*/) const - { - CTestSession* session = new (ELeave) CTestSession(); - CONST_CAST(CTestServer*,this)->iSessionCount++; - return session; - } - -/** - * Constructor - */ -EXPORT_C CTestSession::CTestSession() -: iPersistentStepControl(NULL) -, iPersistentBlockControl(NULL) -, iBlockArray(NULL) - { - } - -/** - * Destructor - */ -EXPORT_C CTestSession::~CTestSession() - { - if( iBlockArray ) - { - delete iBlockArray; - iBlockArray = NULL; - } - - CTestServer* p=(CTestServer*) Server(); - - //delete the persistent step - if(iPersistentStepControl) - delete iPersistentStepControl; - //delete the persistent block - if(iPersistentBlockControl) - delete iPersistentBlockControl; - // Shuts Down the server if this is the last open session - p->SessionClosed(); - } - -/** - * @param aMessage - Function and data for the session - * Session was created by pure virtual CTestServer::NewSessionL() - * Message Functions defined in TestExecuteClient.h - * - * EOpenTestStep - Creates a new subsession - * ERunTestStep - Executes the test step asynchronously - * EAbortTestStep - Kill()'s the executing test step - * ECloseTestStep - Free's the resource - * - * Secure and non-secure variants - * There are two modes of operation: - * Test step is opened with the shared data boolean set to FALSE: - * Create a new CStepControl instance and hence a new thread in its own heap - * Consecutive or Concurrent operation - * Test step is opened with the shared data boolean set to TRUE: - * Create a CPersistentStepControl and keep reusing it, and its thread - * Consecutive operation only - */ -EXPORT_C void CTestSession::ServiceL(const RMessage2& aMessage) - { - switch(aMessage.Function()) - { - case EOpenTestStep : - { - // Open the test step - // Buffer size policed on the client side - TBuf stepName; - // Read the step name from the descriptor - TBool sharedData; - aMessage.ReadL(0,stepName); - // Find out what mode we're working in - sharedData = aMessage.Int1(); - // Both types derive from base class and implement pure virtuals - CControlBase* stepControl = NULL; - if(sharedData) - { - // Shared data mode - // Create the instance if it doesn't exist - if(!iPersistentStepControl) - iPersistentStepControl = new (ELeave)CPersistentStepControl(*(CTestServer*)Server()); - stepControl = iPersistentStepControl; - iPersistentStepControl->StepName() = stepName; - } - else - { - // Default operation. Create a new instance - stepControl = new (ELeave)CStepControl(*(CTestServer*)Server(),stepName); - } - // We pass back the address of the CStepControl class which is passed to - // us in all calls on the subsession in Message 3 - TPtrC8 stepRef(REINTERPRET_CAST(TUint8*,&stepControl),sizeof(TInt)); - aMessage.Write(3,stepRef); - aMessage.Complete(KErrNone); - } - break; - case EOpenTestBlock : - { - // Open the test block - // Buffer size policed on the client side - TBuf stepName; - - // Find out what mode we're working in - TBool sharedData = aMessage.Int1(); - // Both types derive from base class and implement pure virtuals - CBlockControlBase* blockControl = NULL; - if(sharedData) - { - // Shared data mode - // Create the instance if it doesn't exist - if(!iPersistentBlockControl) - iPersistentBlockControl = new (ELeave)CPersistentBlockControl(*(CTestServer*)Server()); - blockControl = iPersistentBlockControl; - } - else - { - // Default operation. Create a new instance - blockControl = new (ELeave)CBlockControl(*(CTestServer*)Server()); - } - // We pass back the address of the CStepControl class which is passed to - // us in all calls on the subsession in Message 3 - TPtrC8 blockRef(REINTERPRET_CAST(TUint8*,&blockControl),sizeof(TInt)); - aMessage.Write(3,blockRef); - aMessage.Complete(KErrNone); - } - break; - case ERunTestStep : - { - // Execute the test step - // Buffer size policed on client side - // Message 0 contains the test step arguments - // Message 1 contains a descriptor for the panic string, if the test step panics - TBuf stepArgs; - aMessage.ReadL(0,stepArgs); - - // Get the address of our CStepControl object - CStepControlBase* stepControl = REINTERPRET_CAST(CStepControlBase*,aMessage.Int3()); - // Kick off the test step - // Message completed when the test step completes - // StartL() is mode dependent pure virtual - TRAPD(err,stepControl->StartL(aMessage, stepArgs)); - if(err) - // Complete now if we can't start the test step - // Client has possibly called run before waiting for last completion - aMessage.Complete(err); - } - break; - case ERunTestBlock : - { - // Execute the test step - // Buffer size policed on client side - // Message 0 contains the test step arguments - // Message 1 contains a descriptor for the panic string, if the test step panics - TBuf blockArgs; - aMessage.ReadL(0,blockArgs); - - // Get the test block of commands - HBufC8* itemArray = HBufC8::NewLC( aMessage.GetDesMaxLengthL(2) ); - TPtr8 itemArrayPtr( itemArray->Des() ); - aMessage.ReadL( 2, itemArrayPtr ); - - // Get the address of our CStepControl object - CBlockControlBase* blockControl = REINTERPRET_CAST(CBlockControlBase*,aMessage.Int3()); - // Kick off the test step - // Message completed when the test step completes - // StartL() is mode dependent pure virtual - TRAPD(err,blockControl->StartL(aMessage, blockArgs, itemArrayPtr )); - if(err) - // Complete now if we can't start the test step - // Client has possibly called run before waiting for last completion - aMessage.Complete(err); - - CleanupStack::PopAndDestroy( itemArray ); - } - break; - case EAbortTestStep : - { - CControlBase* control = REINTERPRET_CAST(CControlBase*,aMessage.Int3()); - // Stop is mode dependent pure virtual - control->Stop(); - // Complete synchronously - aMessage.Complete(KErrNone); - break; - } - case ECloseTestStep : - { - CControlBase* control = REINTERPRET_CAST(CControlBase*,aMessage.Int3()); - // Only delete if we are in non-shared data mode - if( control != iPersistentStepControl && - control != iPersistentBlockControl ) - { - delete control; - } - aMessage.Complete(KErrNone); - } - break; - default: - break; - } - } - -/** - * @param aServer - Reference to the CTestServer base class - * @param aStepName - The test step name - * The Non-Shared data step control implementation - */ -CStepControl::CStepControl(CTestServer& aServer, const TDesC& aStepName) : - CStepControlBase(aServer) - { - StepName() = aStepName; - } - -/** - * Thread completion - */ -void CStepControl::RunL() - { - // Error value if set in the test step will be saved in the Message() - if (Error() != KNull) - { - TBuf errorParam(KErrorEquals); - errorParam.Append(Error()); - Message().Write(1,errorParam); - } - - // If the thread panicked, pick up the panic string and return it to the client - // Overwrites the error value previously saved in Message() - if(WorkerThread().ExitType() == EExitPanic) - { - TBuf panicParam(KPanicEquals); - panicParam.Append(WorkerThread().ExitCategory()); - Message().Write(1,panicParam); - } - - - - if (WorkerThread().ExitType() == EExitPanic) - { - TInt err = WorkerThread().ExitReason(); - SytemWideErrToTefErr(err); - Message().Complete(err); - } - else - { - if (iStatus.Int() == KErrAbort && TimedOut()) - { - if (Server().LoggerStarted()) - { - Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); - } - } - // iStatus.Int() is the same as the thread ExitReason - Message().Complete(iStatus.Int()); - } - - // Close thread handle - WorkerThread().Close(); - } - -/** - * Destructor - */ -CStepControl::~CStepControl() - { - } - -/** - * Step Execution module which is wrapped within UHEAP macros to trap memory leaks during execution - * @param aStepControl - Is a generic type of class T passed as template parameter. Either CStepControl/CWorkerControl - * for normal execution mode and persistant thread mode for concurrent execution of steps respectively - * @param aStep - Object derived from CTestStep class for execution of test steps both in normal mode and persistent mode - */ -template -void ThreadStepExecutionL(T* aStepControl, CTestStep* aStep) - { - TInt loop = 0; - TBool simulateOOM = EFalse; //ShouldRunOOMTest(); - - FOREVER - { - TInt errRun = KErrNone; - - // Call the CTestStep virtuals - aStep->doTestStepPreambleL(); - - // count cells so we can know how many we leaked - TInt cellsStart = User::CountAllocCells(); - - if (simulateOOM) - { - __UHEAP_MARK; - - // set allocator to fail on the loop'th alloc - aStep->SetHeapFailNext(loop); - } - - aStepControl->TimedOut() = ETrue; - TRAP(errRun, aStep->doTestStepL()); - if (errRun != KErrNone && !simulateOOM) - { - if (errRun == KErrAbort) - { - aStepControl->TimedOut() = EFalse; - } - aStep->doTestStepPostambleL(); - User::Leave(errRun); - } - - TBool finishedCorrectly = EFalse; - // cancel the alloc failures - if (simulateOOM) - { - if ((errRun == KErrNone) && (loop >= 1)) - { - // claims to have finished correctly, and we're not failing every alloc - finishedCorrectly = aStep->CheckForHeapFailNext(); - } - aStep->ResetHeapFailNext(); - } - - aStep->doTestStepPostambleL(); - - TInt cellsEnd = User::CountAllocCells(); - - if (cellsStart < cellsEnd && simulateOOM) - { - // leaked. - TInt leakedCells = cellsEnd - cellsStart; - if (aStepControl->Server().LoggerStarted()) - { - aStepControl->Server().ERR_PRINTF3(_L("On loop number %d we leaked %d cells. About to cause panic."),loop,leakedCells); - } - aStep->SetTestStepResult(EFail); - } - - if (simulateOOM) - { - // panic on leak (alloc nnnnnnnn) - __UHEAP_MARKEND; - } - - // check to see if we finished all OOM testing successfully - if ((errRun == KErrNone) && (simulateOOM) && (finishedCorrectly)) - { - // test completed successfully, or the User::Leave(KErrNoMemory) was trapped by something else. - // Need a cunning solution here. Hmm. Testing to see if the next alloc call fails won't work: - // eg, if a test has 3 allocs, heap currently set to fail every 2nd, this would be number 4, - // and if 2 was masked then we would think we are done. - // - // Fix PDEF115450, remove the line aStep->SetTestStepResult(EPass); and modify the information - // to "Out of memory test completed after %d iterations." - if (aStepControl->Server().LoggerStarted()) - { - aStepControl->Server().INFO_PRINTF2(_L("Out of memory test completed after %d iterations."),loop); - } - break; - } - - // check to see if we should run OOM testing. - if (++loop == 1) - { - // first go. - if (!aStep->ShouldRunOOMTest()) - break; - else - { - if (aStepControl->Server().LoggerStarted()) - { - aStepControl->Server().INFO_PRINTF1(_L("Test passed. About to run Out of Memory testing.")); - } - simulateOOM = ETrue; - aStep->IniAccessLog() = EFalse; - } - } - } - } - -/** - * @param aStepControl - Pointer to the step control object which kicked us off - * The thread code. Just drops through with no reuse. - */ -void ThreadFuncL(CStepControl* aStepControl) - { - // Call the server pure virtual to get a step instance - CTestStep* step = CONST_CAST(CTestServer&,aStepControl->Server()).CreateTestStep(aStepControl->StepName()); - if(!step) - User::Leave(KErrNotFound); - CleanupStack::PushL(step); - // Set up the step base class members - TBool sharedData = EFalse; - step->InitialiseL(aStepControl->Args(), aStepControl->Server().Name(), sharedData); - - ThreadStepExecutionL(aStepControl, step); - - // Return Error value set in test step to log result for comparison - if (step->TestStepError() != 0) - aStepControl->Error().Num(step->TestStepError()); - // EPass is 0 - // All the rest should be TRAP'd - if(step->TestStepResult()) - User::Leave(step->TestStepResult()); - CleanupStack::PopAndDestroy(step); - } - -/** - * @param aParam - Pointer to a CStepControl object - * The thread entry method - */ -TInt ThreadFunc(TAny* aParam) - { - // Create the thread's cleanup stack - CTrapCleanup* cleanup = CTrapCleanup::New(); - if(!cleanup) - return KErrNoMemory; - // Trap it and return the error code to the OS - TRAPD(err, ThreadFuncL(REINTERPRET_CAST(CStepControl*,aParam))); - SytemWideErrToTefErr(err); - delete cleanup; - cleanup = NULL; - return err; - } - -/** - * @param aMessage - Keep a reference for async completion - * @param aStepArgs - The RUN_TEST_STEP arguments - * Secure and non-secure variants - * Kick off the test step in its own thread - * Pure virtual implementation - */ -void CStepControl::StartL(const RMessage2& aMessage, const TDesC& aStepArgs) - { - if(IsActive()) - User::Leave(KErrInUse); - Message() = aMessage; - Args().Copy(aStepArgs); - TBuf<8> heapSizeBuf(KNull); - TUint heapSize(0); - aMessage.ReadL(1,heapSizeBuf); - aMessage.Write(1,KNull); - TLex heapSizeLex; - - if (heapSizeBuf.Length() >=3) - { - if ( heapSizeBuf.Mid(0,2).CompareF(_L("0x")) == 0 ) - { - heapSizeLex.Assign(heapSizeBuf.Mid(2)); - } - else - { - heapSizeLex.Assign(heapSizeBuf); - } - heapSizeLex.Val(heapSize,EHex); - } - - TBuf<50> threadName; - // Unique thread name guaranteed if we use the this pointer plus a random number - // whose seed was initialised to the address of the CTestServer object - _LIT(KWorker,"Worker%d %d"); - threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); - // Create with own heap so system cleans up if we kill it - const TUint KMaxHeapSize = 0x100000; - const TUint KMinSize = KMinHeapSize; - if (heapSize < KMinSize) - heapSize = KMaxHeapSize; ///< Allow a 1Mb max heap - - User::LeaveIfError(WorkerThread().Create(threadName, ThreadFunc, KDefaultStackSize + 0x1000,KMinHeapSize, heapSize,this, EOwnerProcess)); - // Prime ready for completion - SetActive(); - // Use the appropriate variant call to get the thread exit - WorkerThread().Logon(iStatus); - WorkerThread().Resume(); - } - -/** - * Kill the thread if it's still running - * The async completion will be picked up as normal with a KErrAbort status - * Pure virtual implementation - */ -void CStepControl::Stop() - { - if(IsActive()) - WorkerThread().Kill(KErrAbort); - } - -/** - * @param aStepControl - Pointer to the step control object. - * The test step thread. - * We reuse this thread so the test steps can store persistent data in the - * CTestServer derived class - * The thread synchronises with its creator via the CWorkerControl class - * Implementation of the shared data mode control object - */ -void PersistentThreadFuncL(CWorkerControl* aControl) - { - // Thread entry is sync'd with a semaphore - // Caller will Wait on this - // Also set our main sync treq to pending. - // It's completed to let us go in and execute the test step code. - aControl->WorkerStatus() = KRequestPending; - aControl->Semaphore().Signal(); - // Go into the main test step execution loop - for(;;) - { - User::WaitForRequest(aControl->WorkerStatus()); - // Check - if(aControl->WorkerStatus().Int() == KErrAbort) - User::Leave(KErrAbort); - CTestStep* step = CONST_CAST(CTestServer&,aControl->Server()).CreateTestStep(aControl->StepName()); - if(!step) - User::Leave(KErrNotFound); - CleanupStack::PushL(step); - // Set up the step base class members - TBool sharedData = ETrue; - step->InitialiseL(aControl->Args(), aControl->Server().Name(), sharedData); - - ThreadStepExecutionL(aControl, step); - - // Pick up the final result - // Set it in the controlling class - aControl->Result() = step->TestStepResult(); - TBuf lError; - if (step->TestStepError() != 0) - { - lError.Num(step->TestStepError()); - if (lError != KNull) - { - lError.Insert(0,KErrorEquals); - aControl->PersistentError().Copy(lError); - } - } - CleanupStack::PopAndDestroy(step); - // Set our status for the wait at the top of the loop - aControl->WorkerStatus() = KRequestPending; - // Signal the status that our creator will be waiting on - // Creator's thread handle in the control class - TRequestStatus* status = &aControl->Status(); - aControl->ControllerThread().RequestComplete(status,KErrNone); - } - } - -/** - * @param aParam - Pointer to a CTestStep control object - * The thread entry method - */ -TInt PersistentThreadFunc(TAny* aParam) - { - // Create the thread's cleanup stack - CTrapCleanup* cleanup = CTrapCleanup::New(); - - if(!cleanup) - return KErrNoMemory; - // Trap it and return the error code to the OS - //defect 116046 - CWorkerControl* workControl = REINTERPRET_CAST(CWorkerControl*,aParam); - workControl->SetCleanupPtr(cleanup); - TRAPD(err, PersistentThreadFuncL(workControl)); - //END defect 116046 - SytemWideErrToTefErr(err); - delete cleanup; - cleanup = NULL; - return err; - } - -/** - * @param aServer - Reference to the CTestServer derived class - * Constructor - */ -CPersistentStepControl::CPersistentStepControl(CTestServer& aServer) : - CStepControlBase(aServer), - iInitialised(EFalse) - { - } - -/** - * @param aMessage - Client's message for completion - * @param aStepArgs - Arguments to the RUN_xxx_STEP_COMMAND's - * Implementation of base class pure virtual. - * Necessarily complex because of thread reuse. Instantiates 2 classes: - * WorkerMonitor class and WorkerControl class - * WorkerMonitor picks up thread exit and WorkerControl picks up test step - * return value. - */ -void CPersistentStepControl::StartL(const RMessage2& aMessage,const TDesC& aStepArgs) - { - if(IsActive()) - User::Leave(KErrInUse); - Message() = aMessage; - Args().Copy(aStepArgs); - // Check to see if we're reusing the worker thread and classes - if(!iInitialised) - { - // Need to construct the monitor and controller classes - // They are both constructed with a reference to our iStatus - // Either of them can complete us. We check their Active flags in our RunL() - iWorkerControl = new (ELeave) CWorkerControl(Server(),iStatus); - // The worker thread needs our thread handle to RequestComplete us - User::LeaveIfError(iWorkerControl->ControllerThread().Duplicate(RThread())); - // Worker thread entry is sync'd with a semaphore. - User::LeaveIfError(iWorkerControl->Semaphore().CreateLocal(0)); - TBuf<50> threadName; - // Unique thread name guaranteed if we use the this pointer plus a random number - // whose seed was initialised to the address of the CTestServer object - // Create in our heap. - _LIT(KWorker,"Worker%d %d"); - threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); - User::LeaveIfError(WorkerThread().Create(threadName,PersistentThreadFunc, KDefaultStackSize + 0x1000,NULL,iWorkerControl, EOwnerProcess)); - iWorkerMonitor = new (ELeave) CWorkerMonitor(iStatus); - } - // Worker thread needs the step arguments and the step name - iWorkerControl->Args().Set(Args()); - iWorkerControl->StepName().Set(StepName()); - // Set this object ready for completion by either the monitor or controller objects - Prime(); - // Set the child monitor and control objects ready for completion - iWorkerMonitor->SetActive(); - iWorkerControl->Prime(); - // Use the monitor object to pick up thread exit - // This should only happen for panic, leave and abort following the Stop() call - WorkerThread().Rendezvous(iWorkerMonitor->Status()); - if(!iInitialised) - { - // Start the thread and sync up via the semaphore - WorkerThread().Resume(); - iWorkerControl->Semaphore().Wait(); - iWorkerControl->Semaphore().Close(); - iInitialised = ETrue; - } - // Worker thread will be at the top of its loop waiting to execute - // the test step virtuals. - // Issue the request then it will drop through - TRequestStatus* status = &iWorkerControl->WorkerStatus(); - WorkerThread().RequestComplete(status,KErrNone); - } - -/** - * Destructor - */ -CPersistentStepControl::~CPersistentStepControl() - { - // Only need to clean up in the initialised state - if(!iInitialised) - return; - - // Check both objects - // Neither of them should be active, but just in case - if(iWorkerMonitor->IsActive()) - { - // Cancelling means we don't get stray events - WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); - // Need to cancel the objeect itself - iWorkerMonitor->Cancel(); - } - if(iWorkerControl->IsActive()) - { - // Complete the request then cancel - TRequestStatus* status = &iWorkerControl->Status(); - User::RequestComplete(status,KErrNone); - iWorkerControl->Cancel(); - } - - // The worker thread will currently be blocked on its TRequestStatus at the top - // of its loop. - // Signal the status with KErrAbort and the thread will check this value and leave. - // If we Kill the thread then the cleanup stack for the thread is orphaned. - // PersistentThreadFuncL() TRAP's the leave. - // We logon and catch the thread exit. - TRequestStatus status = KRequestPending; - WorkerThread().Rendezvous(status); - TRequestStatus* workerStatus = &iWorkerControl->WorkerStatus(); - WorkerThread().RequestComplete(workerStatus,KErrAbort); - User::WaitForRequest(status); - // Close both handles - WorkerThread().Close(); - iWorkerControl->ControllerThread().Close(); - - delete iWorkerControl; - delete iWorkerMonitor; - } - -/** - * Necessarily complex because of the two sources of completion - * We can tell which one completed us by checking their iActive members - */ -void CPersistentStepControl::RunL() - { - if (iWorkerControl->PersistentError() != KNull) - { - TBuf errorParam; - errorParam.Copy(iWorkerControl->PersistentError()); // Error Value returned as Panic Result - Message().Write(1,errorParam); - iWorkerControl->PersistentError().Copy(KNull); - } - TInt ret = KErrNone; - // Check which of the child objects completed us - if(!iWorkerMonitor->IsActive()) - { - // Unexpected exit from the worker thread - iInitialised = EFalse;// this also make ~CPersistentStepControl not to delete twice. - // Pick up the exit reason and panic code if it exists - if(WorkerThread().ExitType() == EExitPanic) - { - TBuf panicParam(KPanicEquals); - panicParam.Append(WorkerThread().ExitCategory()); // Panic Value returned as Result - - //START defect 116046, Cleanup memories. - //iWorkerControl->Cleanup(); - //END defect 116046 - - Message().Write(1,panicParam); - } - - ret = WorkerThread().ExitReason(); - - if (WorkerThread().ExitType() == EExitPanic) - { - SytemWideErrToTefErr(ret); - } - if (ret == KErrAbort && iWorkerControl->TimedOut()) - { - if (Server().LoggerStarted()) - { - Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); - } - } - - // We need to complete and cancel the other request so we don't have stray events - TRequestStatus* status = &iWorkerControl->Status(); - User::RequestComplete(status,KErrNone); - iWorkerControl->Cancel(); - // Free the resource in the worker control object - iWorkerControl->ControllerThread().Close(); - WorkerThread().Close(); - delete iWorkerControl; - iWorkerControl = NULL; - delete iWorkerMonitor; - iWorkerMonitor = NULL; - // Next time in to StartL() we create them from cleana - } - else if(!iWorkerControl->IsActive()) - { - // Normal test step completion - // We can reuse the thread next time into StartL() - // The thread will be blocking on iWorkerStatus - // We need to cancel the other object - WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); - iWorkerMonitor->Cancel(); - // Retrieve the test result - ret = iWorkerControl->Result(); - SytemWideErrToTefErr(ret); - } - else - // Unexpected - { - ret = iStatus.Int(); - } - // Complete back to the client - Message().Complete(ret); - } - -/** - * Abort due to timeout - * The worker monitor object will pick up the thread exit - */ -void CPersistentStepControl::Stop() - { - if(iWorkerMonitor->IsActive()) - { - WorkerThread().Kill(KErrAbort); - } - } - - -CBlockControlBase::~CBlockControlBase() - { - if( iBlockArray ) - { - delete iBlockArray; - iBlockArray = NULL; - } - } - -TTEFItemArray* CBlockControlBase::BlockArray() const - { - return iBlockArray; - } - -void CBlockControlBase::CreateBlockArrayL( const TDesC8& aBlockArrayPckg ) - { - if( iBlockArray ) - { - delete iBlockArray; - iBlockArray = NULL; - } - - TTEFItemPkgBuf itemPckgBuf; - TInt count = aBlockArrayPckg.Size()/itemPckgBuf.Size(); - iBlockArray = new (ELeave) TTEFItemArray( count ); - TInt pos = 0; - for( TInt i=0; iAppendL( itemPckgBuf() ); - } - } - -HBufC8* CBlockControlBase::CreateBlockArrayPckgLC() - { - TInt count = iBlockArray->Count(); - TTEFItemPkgBuf itemPckgBuf; - HBufC8* blockArrayPckg = HBufC8::NewLC( count * itemPckgBuf.Size() ); - TPtr8 ptr( blockArrayPckg->Des() ); - for( TInt i=0; iAt(i); - ptr.Append( itemPckgBuf ); - } - - return blockArrayPckg; - } - -CBlockControl::CBlockControl(CTestServer& aServer) -: CBlockControlBase(aServer) - { - } - -CBlockControl::~CBlockControl() - { - } - -void CBlockControl::RunL() - { - // Write back the test block - HBufC8* blockArrayPckg = CreateBlockArrayPckgLC(); - TPtr8 blockArrayPtr(blockArrayPckg->Des()); - Message().WriteL( 2, blockArrayPtr ); - CleanupStack::PopAndDestroy(blockArrayPckg); - - // If the thread panicked, pick up the panic string and return it to the client - // Overwrites the error value previously saved in Message() - if(WorkerThread().ExitType() == EExitPanic) - { - TBuf panicParam(KPanicEquals); - TPtrC panicCat = WorkerThread().ExitCategory(); // Panic Value returned as Result - if( panicCat.Length() == 0 ) - { - panicParam.Append(_L("NULL")); - } - panicParam.Append( panicCat ); - Message().Write(1,panicParam); - } - - if (WorkerThread().ExitType() == EExitPanic) - { - TInt err = WorkerThread().ExitReason(); - SytemWideErrToTefErr(err); - Message().Complete(err); - } - else - { - if (iStatus.Int() == KErrAbort && TimedOut()) - { - if (Server().LoggerStarted()) - { - Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); - } - } - // iStatus.Int() is the same as the thread ExitReason - Message().Complete(iStatus.Int()); - } - - // Close thread handle - WorkerThread().Close(); - } - - -void ThreadBlockFuncL(CBlockControl* aBlockControl) - { - // Call the server pure virtual to get a step instance - CTestBlockController* block = REINTERPRET_CAST(CTestServer2&,aBlockControl->Server()).CreateTestBlock(); - if(!block) - { - User::Leave(KErrNotFound); - } - CleanupStack::PushL(block); - - // Set up the block base class members - TBool sharedData = EFalse; - block->InitialiseL(aBlockControl->Args(), aBlockControl->Server().Name(), sharedData); - block->SetBlockArray(aBlockControl->BlockArray()); - block->SetSharedData( REINTERPRET_CAST(CTestServer2*, &aBlockControl->Server() )); - - ThreadStepExecutionL(aBlockControl, block); - - // All the rest should be TRAP'd - if( block->TestStepResult() ) - { - User::Leave( block->TestStepResult() ); - } - - CleanupStack::PopAndDestroy(block); - } - -TInt ThreadBlockFunc(TAny* aParam) - { - // Create the thread's cleanup stack - CTrapCleanup* cleanup = CTrapCleanup::New(); - if(!cleanup) - return KErrNoMemory; - // Trap it and return the error code to the OS - TRAPD(err, ThreadBlockFuncL(REINTERPRET_CAST(CBlockControl*,aParam))); - SytemWideErrToTefErr(err); - delete cleanup; - cleanup = NULL; - return err; - } - -void CBlockControl::StartL(const RMessage2& aMessage,const TDesC& aArgs, const TDesC8& aBlockArrayPckg) - { - if(IsActive()) - { - User::Leave(KErrInUse); - } - - Message() = aMessage; - Args().Copy(aArgs); - TBuf<8> heapSizeBuf(KNull); - TUint heapSize(0); - aMessage.ReadL(1,heapSizeBuf); - aMessage.Write(1,KNull); - TLex heapSizeLex; - - if (heapSizeBuf.Length() >=3) - { - if ( heapSizeBuf.Mid(0,2).CompareF(_L("0x")) == 0 ) - { - heapSizeLex.Assign(heapSizeBuf.Mid(2)); - } - else - { - heapSizeLex.Assign(heapSizeBuf); - } - heapSizeLex.Val(heapSize,EHex); - } - - // Set the BlockArray so it can be passed and used by the TestBlockController - CreateBlockArrayL( aBlockArrayPckg ); - - // Unique thread name guaranteed if we use the this pointer plus a random number - // whose seed was initialised to the address of the CTestServer object - TBuf<50> threadName; - _LIT(KWorker,"Worker%d %d"); - threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); - - // Create with own heap so system cleans up if we kill it - if( (TInt)heapSize < KMinHeapSize ) - { - heapSize = KDefaultHeapSize; ///< Allow a 1Mb max heap - } - - User::LeaveIfError(WorkerThread().Create(threadName, ThreadBlockFunc, KDefaultStackSize + 0x1000,KMinHeapSize, heapSize, this, EOwnerProcess)); - - // Prime ready for completion - SetActive(); - // Use the appropriate variant call to get the thread exit - WorkerThread().Logon(iStatus); - WorkerThread().Resume(); - } - -void CBlockControl::Stop() - { - if(IsActive()) - { - WorkerThread().Kill(KErrAbort); - } - } - -CPersistentBlockControl::CPersistentBlockControl(CTestServer& aServer) -: CBlockControlBase(aServer) -, iWorkerControl(NULL) -, iWorkerMonitor(NULL) -, iInitialised(EFalse) - { - } - -CPersistentBlockControl::~CPersistentBlockControl() - { - - // Only need to clean up in the initialised state - if(!iInitialised) - return; - - // Check both objects - // Neither of them should be active, but just in case - if(iWorkerMonitor->IsActive()) - { - // Cancelling means we don't get stray events - WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); - // Need to cancel the objeect itself - iWorkerMonitor->Cancel(); - } - if(iWorkerControl->IsActive()) - { - // Complete the request then cancel - TRequestStatus* status = &iWorkerControl->Status(); - User::RequestComplete(status,KErrNone); - iWorkerControl->Cancel(); - } - - // The worker thread will currently be blocked on its TRequestStatus at the top - // of its loop. - // Signal the status with KErrAbort and the thread will check this value and leave. - // If we Kill the thread then the cleanup stack for the thread is orphaned. - // PersistentThreadFuncL() TRAP's the leave. - // We logon and catch the thread exit. - TRequestStatus status = KRequestPending; - WorkerThread().Rendezvous(status); - TRequestStatus* workerStatus = &iWorkerControl->WorkerStatus(); - WorkerThread().RequestComplete(workerStatus,KErrAbort); - User::WaitForRequest(status); - // Close both handles - WorkerThread().Close(); - iWorkerControl->ControllerThread().Close(); - - delete iWorkerControl; - delete iWorkerMonitor; - } - -void PersistentThreadBlockFuncL(CBlockWorkerControl* aControl) - { - // Thread entry is sync'd with a semaphore - // Caller will Wait on this - // Also set our main sync treq to pending. - // It's completed to let us go in and execute the test step code. - aControl->WorkerStatus() = KRequestPending; - aControl->Semaphore().Signal(); - // Go into the main test step execution loop - for(;;) - { - User::WaitForRequest(aControl->WorkerStatus()); - // Check - if(aControl->WorkerStatus().Int() == KErrAbort) - User::Leave(KErrAbort); - CTestBlockController* block = REINTERPRET_CAST(CTestServer2&, aControl->Server()).CreateTestBlock(); - if(!block) - User::Leave(KErrNotFound); - CleanupStack::PushL(block); - // Set up the step base class members - TBool sharedData = ETrue; - block->InitialiseL(aControl->Args(), aControl->Server().Name(), sharedData); - - block->SetBlockArray( aControl->BlockArray() ); - block->SetSharedData( REINTERPRET_CAST(CTestServer2*, &aControl->Server()) ); - - ThreadStepExecutionL(aControl, block); - - // Pick up the final result - // Set it in the controlling class - aControl->Result() = block->TestStepResult(); - TBuf lError; - if (block->TestStepError() != 0) - lError.Num(block->TestStepError()); - if (lError != KNull) - { - lError.Insert(0,KErrorEquals); - aControl->PersistentError().Copy(lError); - } - CleanupStack::PopAndDestroy(block); - // Set our status for the wait at the top of the loop - aControl->WorkerStatus() = KRequestPending; - // Signal the status that our creator will be waiting on - // Creator's thread handle in the control class - TRequestStatus* status = &aControl->Status(); - aControl->ControllerThread().RequestComplete(status,KErrNone); - } - } - -TInt PersistentThreadBlockFunc(TAny* aParam) - { - // Create the thread's cleanup stack - CTrapCleanup* cleanup = CTrapCleanup::New(); - if(!cleanup) - return KErrNoMemory; - // Trap it and return the error code to the OS - TRAPD(err, PersistentThreadBlockFuncL(REINTERPRET_CAST(CBlockWorkerControl*,aParam))); - SytemWideErrToTefErr(err); - delete cleanup; - cleanup = NULL; - return err; - } - -void CPersistentBlockControl::StartL(const RMessage2& aMessage,const TDesC& aStepArgs, const TDesC8& aBlockArrayPckg) - { - if(IsActive()) - User::Leave(KErrInUse); - Message() = aMessage; - Args().Copy(aStepArgs); - - // Set the BlockArray so it can be passed and used by the TestBlockController - // Set the BlockArray so it can be passed and used by the TestBlockController - CreateBlockArrayL( aBlockArrayPckg ); - - // Check to see if we're reusing the worker thread and classes - if(!iInitialised) - { - // Need to construct the monitor and controller classes - // They are both constructed with a reference to our iStatus - // Either of them can complete us. We check their Active flags in our RunL() - iWorkerControl = new (ELeave) CBlockWorkerControl(Server(),iStatus); - // The worker thread needs our thread handle to RequestComplete us - User::LeaveIfError(iWorkerControl->ControllerThread().Duplicate(RThread())); - // Worker thread entry is sync'd with a semaphore. - User::LeaveIfError(iWorkerControl->Semaphore().CreateLocal(0)); - TBuf<50> threadName; - // Unique thread name guaranteed if we use the this pointer plus a random number - // whose seed was initialised to the address of the CTestServer object - // Create in our heap. - _LIT(KWorker,"Worker%d %d"); - threadName.Format(KWorker,(TInt)this,Math::Rand(CONST_CAST(CTestServer&,Server()).RandSeed())); - User::LeaveIfError(WorkerThread().Create(threadName,PersistentThreadBlockFunc, KDefaultStackSize + 0x1000,NULL,iWorkerControl, EOwnerProcess)); - iWorkerMonitor = new (ELeave) CWorkerMonitor(iStatus); - } - - // Set the Block Array - iWorkerControl->SetBlockArray( BlockArray() ); - - // Worker thread needs the step arguments and the step name - iWorkerControl->Args().Set(Args()); - - // Set this object ready for completion by either the monitor or controller objects - Prime(); - // Set the child monitor and control objects ready for completion - iWorkerMonitor->SetActive(); - iWorkerControl->Prime(); - // Use the monitor object to pick up thread exit - // This should only happen for panic, leave and abort following the Stop() call - WorkerThread().Rendezvous(iWorkerMonitor->Status()); - if(!iInitialised) - { - // Start the thread and sync up via the semaphore - WorkerThread().Resume(); - iWorkerControl->Semaphore().Wait(); - iWorkerControl->Semaphore().Close(); - iInitialised = ETrue; - } - // Worker thread will be at the top of its loop waiting to execute - // the test step virtuals. - // Issue the request then it will drop through - TRequestStatus* status = &iWorkerControl->WorkerStatus(); - WorkerThread().RequestComplete(status,KErrNone); - } - -void CPersistentBlockControl::RunL() - { - // Write back the test block - HBufC8* blockArrayPckg = CreateBlockArrayPckgLC(); - TPtr8 blockArrayPtr(blockArrayPckg->Des()); - Message().WriteL( 2, blockArrayPtr ); - CleanupStack::PopAndDestroy(blockArrayPckg); - - if (iWorkerControl->PersistentError() != KNull) - { - TBuf errorParam; - errorParam.Copy(iWorkerControl->PersistentError()); // Error Value returned as Panic Result - Message().Write(1,errorParam); - iWorkerControl->PersistentError().Copy(KNull); - } - TInt ret = KErrNone; - // Check which of the child objects completed us - if(!iWorkerMonitor->IsActive()) - { - // Unexpected exit from the worker thread - iInitialised = EFalse;// this also make ~CPersistentBlockControl not to delete twice. - // Pick up the exit reason and panic code if it exists - if(WorkerThread().ExitType() == EExitPanic) - { - TBuf panicParam(KPanicEquals); - TPtrC panicCat = WorkerThread().ExitCategory(); // Panic Value returned as Result - if( panicCat.Length() == 0 ) - { - panicParam.Append(_L("NULL")); - } - panicParam.Append( panicCat ); - Message().Write(1,panicParam); - } - - ret = WorkerThread().ExitReason(); - - if (WorkerThread().ExitType() == EExitPanic) - { - SytemWideErrToTefErr(ret); - } - if (ret == KErrAbort && iWorkerControl->TimedOut()) - { - if (Server().LoggerStarted()) - { - Server().ERR_PRINTF1(_L("TEST IS ABOUT TO ABORT DUE TO TEF TIMEOUT")); - } - } - - // We need to complete and cancel the other request so we don't have stray events - TRequestStatus* status = &iWorkerControl->Status(); - User::RequestComplete(status,KErrNone); - iWorkerControl->Cancel(); - // Free the resource in the worker control object - iWorkerControl->ControllerThread().Close(); - WorkerThread().Close(); - delete iWorkerControl; - iWorkerControl = NULL; - delete iWorkerMonitor; - iWorkerMonitor = NULL; - // Next time in to StartL() we create them from cleana - } - else if(!iWorkerControl->IsActive()) - { - // Normal test step completion - // We can reuse the thread next time into StartL() - // The thread will be blocking on iWorkerStatus - // We need to cancel the other object - WorkerThread().RendezvousCancel(iWorkerMonitor->Status()); - iWorkerMonitor->Cancel(); - // Retrieve the test result - ret = iWorkerControl->Result(); - SytemWideErrToTefErr(ret); - } - else - // Unexpected - { - ret = iStatus.Int(); - } - // Complete back to the client - Message().Complete(ret); - } - -void CPersistentBlockControl::Stop() - { - if(iWorkerMonitor->IsActive()) - { - WorkerThread().Kill(KErrAbort); - } - } - -/** - * //> @internalComponent - * @param aErr - Reference to the error number - * this delling with Leave or set result error number conflict with sys wide numbers[-1,-49] - */ -void SytemWideErrToTefErr(TInt &aErr) -{ - switch (aErr) - { - case KErrInUse: - { - aErr = KErrTestExecuteInUse; - } - break; - case KErrServerBusy: - { - aErr = KErrTestExecuteServerBusy; - } - break; - }; - return; -}