testexecfw/stf/stfext/testmodules/teftestmod/teftestmodulefw/utils/src/testserverbase.cpp
changeset 2 8bb370ba6d1d
--- /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;
+}