--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/testexecfw/stf/stfext/testmodules/teftestmod/teftestmodulefw/utils/src/testserverbase.cpp Fri Apr 09 10:46:28 2010 +0800
@@ -0,0 +1,1482 @@
+/*
+* 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 <e32math.h>
+#include <test/wrapperutilsplugin.h>
+#include <test/tefutils.h>
+
+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<KMaxTestExecuteNameLength> resultFilePath;
+ TBuf<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> xmlLogFile(xmlFilePath);
+ TBuf<KMaxTestExecuteNameLength> logFile;
+ TBuf<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteLogFilePath> 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<KMaxTestStepNameLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteCommandLength> 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<KMaxTestExecuteCommandLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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<class T>
+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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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; i<count; i++ )
+ {
+ itemPckgBuf.Copy(aBlockArrayPckg.Mid(pos, itemPckgBuf.Size()));
+ pos += itemPckgBuf.Size();
+ iBlockArray->AppendL( 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; i<count; i++ )
+ {
+ itemPckgBuf = iBlockArray->At(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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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<KMaxTestExecuteNameLength> 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;
+}