mmtestenv/mmtestfw/Source/TestFramework/parseline.cpp
changeset 0 40261b775718
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mmtestenv/mmtestfw/Source/TestFramework/parseline.cpp	Tue Feb 02 01:56:55 2010 +0200
@@ -0,0 +1,2274 @@
+// Copyright (c) 2002-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:
+// This module contains CParseLine and CSuiteDll classes
+// CParseLine contains the functions required to execute
+// a line of test script file.
+// CSuiteDll objects contains information about test suite
+// dlls that have been loaded.
+// 
+//
+
+// system includes
+#include <f32file.h>
+
+// test system includes
+#include "TestFramework.h"
+#include "script.h"
+#include "parseline.h"
+#include "Filename.h" 
+
+#include "parseline.inl"
+
+const TInt KTimeIncrement = 100*1000000; // max wait interval in micro-seconds (100s)
+const TUint KMaxThreadAttempts = 128;	// max number of times to attempt to create a thread
+
+/**
+ *
+ * File path literals
+ *
+ * @xxxx
+ *
+ */
+_LIT(KTxtDLLpath, "c:\\;c:\\system\\libs;d:\\;d:\\system\\libs;e:\\;e:\\system\\libs;z:\\;z:\\system\\libs");
+
+/**
+ *
+ * Script parameter defaults
+ *
+ * @xxxx
+ *
+ */
+//const TInt KTestGuardTimerDefault = 1000L;	// EABI warning removal
+const TInt KPanicGuardTimerDefault = 1000000L;
+const TInt KPanicExitReasonDefault = 0;
+
+/**
+ *
+ * CParseLine first-phase constructor
+ *
+ * @xxxx
+ *
+ */
+CParseLine::CParseLine(const TDesC& aMatchString)
+	:iTestVerdict(EPass), iMatchString(aMatchString)
+	{
+	}
+
+/**
+ *
+ * CParseLine second-phase constructor 
+ *
+ * @param	"CScript* aScript"
+ *			The script to be parsed
+ *
+ * @param	"CTestUtils* aTestUtils"
+ *			The TestUtils object to use
+ *
+ * @param	"CLog* aLog"
+ *			The logger to use
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::ConstructL(CScript* aScript, CTestUtils* aTestUtils, CLog* aLog, TInt64 aGuardTimer)
+	{
+	// create a new Array to store the test steps in
+	iArrayLoadedSuiteDll = new(ELeave) CArrayPtrFlat<CSuiteDll>(1);
+
+	iScript = aScript;
+	iTestUtils = aTestUtils;
+	iLog = aLog;
+	iGuardTimer = aGuardTimer;
+	iSeverity = ESevrAll;
+	iBreakOnError = EFalse;
+	}
+
+/**
+ *
+ * CParseLine static constructor 
+ *
+ * @param	"CScript* aScript"
+ *			The script to be parsed
+ *
+ * @param	"CTestUtils* aTestUtils"
+ *			The TestUtils object to use
+ *
+ * @param	"CLog* aLog"
+ *			The logger to use
+ *
+ * @xxxx
+ *
+ */
+CParseLine* CParseLine::NewL(CScript* aScript, CTestUtils* aTestUtils, CLog* aLog, TInt64 aGuardTimer, const TDesC& aMatchString)
+	{
+	CParseLine* self = new(ELeave) CParseLine(aMatchString);
+	CleanupStack::PushL(self);
+	self->ConstructL(aScript, aTestUtils, aLog, aGuardTimer);
+	CleanupStack::Pop();
+	return self;
+	}
+
+/**
+ *
+ * CParseLine destructor 
+ *
+ * @xxxx
+ *
+ */
+CParseLine::~CParseLine()
+	{
+
+	// unload DLLs and their records
+	if (iArrayLoadedSuiteDll)
+		{
+		// delete all objects in iArrayLoadedSuiteDll
+		// the destructors will unload any loaded DLLS
+		iArrayLoadedSuiteDll->ResetAndDestroy();
+		delete iArrayLoadedSuiteDll;
+		}
+
+	}
+
+/**
+ *
+ * Process a single line from the script file.
+ *
+ * @param	"const TDesC8& aNarrowline"
+ *			The script line
+ *
+ * @param	"TInt8 aLineNo"
+ *			The script line number
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::ProcessLineL(const TDesC8& aNarrowline, TInt aLineNo)
+	{
+	// make a local unicode buffer
+	TPtr16 lineBuf(REINTERPRET_CAST(TUint16*,User::AllocLC(KMaxLenScriptLine*2)), 0, KMaxLenScriptLine);
+	lineBuf.Fill('\0', KMaxLenScriptLine);
+
+	// convert the narrow script file to Unicode
+	// TBC find a better way to do this
+	CFileName* testnameU = CFileName::NewLC();
+	testnameU->Copy(aNarrowline);
+
+	// find the end of the line
+	TInt end = testnameU->Locate('\n');
+
+	// copy the line into lineBuf
+	if ((end != KErrNotFound) && (end < KMaxLenScriptLine))
+		lineBuf = testnameU->Left(end - 1);
+	else
+		lineBuf = testnameU->FileName();
+
+	// destroy filename
+	CleanupStack::PopAndDestroy(testnameU);
+
+	// the parser relies on spaces between tokens. Commas are
+	// allowed but are just replaced with spaces
+	TInt findComma = lineBuf.Locate(TChar(','));
+	while (findComma != KErrNotFound )
+		{
+		// found a comma so replace with space
+		lineBuf.Replace(findComma, 1, _L(" "));
+		findComma = lineBuf.Locate(TChar(','));
+		}
+
+	// for debugging display the line with a line no
+#ifdef SCRIPT_DEBUG
+	INFO_PRINTF3(_L("Line:%d %S "), aLineNo, &lineBuf);
+#endif
+
+	// if there has been a failure and the user has selected
+	// x then the next commands in the script are skipped until
+	// a test complete statement is found
+	if (iBreakOnError)
+		{
+		if (lineBuf.FindF(_L("TEST_COMPLETE")) == 0)
+			{
+			TestComplete(lineBuf);
+			// reset flag now test complete found
+			iBreakOnError = EFalse;
+			}
+
+		CleanupStack::PopAndDestroy(); // linebuf
+		// do not process the rest of the line
+		return;
+		}
+
+	// check the line for command keywords
+	if ((lineBuf.Find(_L("//")) == 0) || (lineBuf.Find(_L("#")) == 0))
+		{
+		// ignore comments
+		}
+	else if (lineBuf.FindF(_L("LOAD_SUITE")) == 0)
+		{
+		LoadSuiteL(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("RUN_SCRIPT")) == 0)
+		{
+		RunScriptL(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("RUN_TEST_STEP")) == 0)
+		{
+		RunTestStep(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("RUN_PANIC_STEP")) == 0)
+		{
+		RunPanicTestStep(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("RUN_TERMINATION_STEP")) == 0)
+		{
+		RunTerminationTestStep(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("RUN_UTILS")) == 0)
+		{
+		RunUtil(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("RUN_PROGRAM")) == 0)
+		{
+		RunProgram(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("UNLOAD")) == 0)
+		{
+		Unload();
+		}
+	else if (lineBuf.FindF(_L("HEAP_MARK")) == 0)
+		{
+		HeapMark();
+		}
+	else if (lineBuf.FindF(_L("HEAP_CHECK")) == 0)
+		{
+		HeapCheck();
+		}
+	else if (lineBuf.FindF(_L("REQUEST_MARK")) == 0)
+		{
+		RequestMark();	
+		}
+	else if (lineBuf.FindF(_L("REQUEST_CHECK")) == 0)
+		{
+		RequestCheck();
+		}
+	else if (lineBuf.FindF(_L("HANDLES_MARK")) == 0)
+		{
+		HandlesMark();
+		}
+	else if (lineBuf.FindF(_L("HANDLES_CHECK")) == 0)
+		{
+		HandlesCheck();
+		}
+	else if (lineBuf.FindF(_L("PRINT")) == 0)
+		{
+		ScriptPrint(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("DELAY")) == 0)
+		{
+		Delay(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("SEVERITY")) == 0)
+		{
+		SetSeverity(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("PAUSE_AT_END")) == 0)
+		{
+		// if implemented, add iScript->iPauseAtEnd = ETrue;
+		WARN_PRINTF1(_L("Warning : PAUSE_AT_END not implemented"));
+		}
+	else if (lineBuf.FindF(_L("MULTITHREAD")) == 0)
+		{
+		WARN_PRINTF1(_L("Warning : MULTITHREAD keyword no longer required"));
+		}
+	else if (lineBuf.FindF(_L("SINGLETHREAD")) == 0)
+		{
+		ERR_PRINTF1(_L("Error : Single thread operation no longer supported"));
+		}
+	else if (lineBuf.FindF(_L("PAUSE")) == 0)
+		{
+		iScript->Pause();
+		}
+	else if (lineBuf.FindF(_L("BREAK_ON_ERROR")) == 0)
+		{
+		// if the current test verdict is not PASS
+		// give the user the chance to quit
+		if ( iTestVerdict != EPass )
+			iBreakOnError = iScript->BreakOnError();
+		}
+	else if (lineBuf.FindF(_L("TEST_COMPLETE")) == 0)
+		{
+		// use Tlex to decode the cmd line
+		TestComplete(lineBuf);
+		}
+	else if (lineBuf.FindF(_L("LOG_SETTINGS")) == 0)
+		{
+		// use Tlex to decode the cmd line
+		LogSettings(lineBuf);
+		}
+	else if (lineBuf.Length() == 0)
+		{
+		// ignore blank lines
+		}
+	else
+		{
+		// failed to decode line
+		ERR_PRINTF3(_L("Error in script line:%d - \'%S\'"), aLineNo, &lineBuf);
+		}
+
+	CleanupStack::PopAndDestroy(); // linebuf
+	}
+
+/**
+ *
+ * Implements the TEST_COMPLETE script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::TestComplete(const TDesC& aText)
+	{
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// start at the begining
+	TPtrC token = lex.NextToken();
+
+	// get suite name, if any
+	token.Set(lex.NextToken());
+
+	if (token.Length() != 0)
+		{
+		TBuf<KMaxLenTestSuiteName> currentSuiteName;
+		currentSuiteName = token;
+
+		// get step name, if any
+		token.Set(lex.NextToken());
+	
+		if (token.Length() != 0)
+			{	
+			iCurrentStepName = token;
+			iCurrentSuiteName = currentSuiteName;
+			}
+		else
+			{
+			// failed to decode line - require 0 or 2 parameters exactly
+			// use last suite/step name, return fail
+			ERR_PRINTF2(_L("Error in script line: \'%S\'"), &aText);
+			iTestVerdict = EFail;
+			}
+		}
+
+	if (!iSkip)
+		{
+		// add the current result to the script
+		iScript->AddResult(iTestVerdict);		
+		}
+		
+	// reset for next test
+	iTestVerdict = EPass;
+	}
+
+/**
+ *
+ * Implements the PRINT script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::ScriptPrint(const TDesC& aText)
+	{
+	// display the text after the PRINT and 1 space = 6
+	INFO_PRINTF2(_L("%s "), (aText.Ptr() + 6));
+	}
+
+/**
+ *
+ * Implements the DELAY script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::Delay(const TDesC& aText)
+	{
+	// if the test has already failed skip the delay
+	if (iTestVerdict != EPass)
+		{
+		WARN_PRINTF1(_L("Skipped delay as test has already failed"));
+		return;
+		}
+
+	// get the required time for the delay
+	// first get the value as a string
+	TLex timeOut(aText);
+	timeOut.NextToken();
+	TPtrC token = timeOut.NextToken();
+
+	// convert the value into a TInt
+	TLex lexTime(token);
+	TInt64 guardTimerValue;
+	if (lexTime.Val(guardTimerValue) != KErrNone  )
+		{
+		ERR_PRINTF2(_L("Error in guard timer value : could not decode \'%S\' as value"), 
+					&token);
+		return;
+		}
+
+	INFO_PRINTF2(_L("Delay for %ld mS"), guardTimerValue);
+
+	// wait for the required delay
+	User::After(I64INT(guardTimerValue) * 1000);
+	}
+	
+/**
+ *
+ * Implements the SEVERITY script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::SetSeverity(const TDesC& aText)
+	{
+	// get the required time for the delay
+	// first get the value as a string
+	TLex severityOut(aText);
+	severityOut.NextToken();
+	TPtrC token = severityOut.NextToken();
+
+	// convert the value into a TInt
+	TLex lexSeverity(token);
+	TInt severityValue = ESevrAll;
+	if (lexSeverity.Val(severityValue) != KErrNone)
+		{
+		ERR_PRINTF2(_L("Error in severity level value : could not decode \'%S\' as value"),
+					&token);
+		return;
+		}
+
+	// check severity value to ensure that only bitmasks in use are set...
+	if(!LogSeverity::IsValid(severityValue))
+		{
+		ERR_PRINTF1(_L("Error in severity value : out of range"));
+		return;
+		}
+	else
+		{
+		iSeverity = severityValue;
+
+		TInt noOfDlls = iArrayLoadedSuiteDll->Count();
+		for ( TInt i = 0; i < noOfDlls; i++)
+			{
+			CSuiteDll* ptrSuite = iArrayLoadedSuiteDll->At(i);
+			CTestSuite* testSuite = ptrSuite->Suite();
+			testSuite->SetSeverity(iSeverity);
+			}
+		}
+
+	INFO_PRINTF2(_L("Severity is set to %d"), severityValue);
+	}
+
+/**
+ *
+ * Implements the RUN_SCRIPT script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RunScriptL(const TDesC& aText)
+	{
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// start at the begining
+	TPtrC token=lex.NextToken();
+
+	// step over the keyword
+	token.Set(lex.NextToken());
+
+	// format for printing
+	INFO_PRINTF2(_L("RUN_SCRIPT %S"), &token);
+
+	// create a new Script object (but use the current parser
+	// as it has the dll loaded record)
+	CScript* newScript=CScript::NewLC(this, iTestUtils, iLog, iGuardTimer, iMatchString);
+
+	// read in the script file
+	CFileName* scriptFileName = CFileName::NewLC();
+	*scriptFileName = token;
+
+	if (newScript->OpenScriptFile(scriptFileName))
+		{
+		// process it
+		iTestVerdict = newScript->ExecuteScriptL();
+
+		// don't bother logging verdicts for scripts - not really useful
+		// add results from the new script to the owner script
+		iScript->AddResult(newScript);
+		}
+	else
+		{
+		// failed to find script so verdict incloncusive
+		iTestVerdict = EInconclusive;
+		}
+
+	CleanupStack::PopAndDestroy(scriptFileName);
+	CleanupStack::PopAndDestroy(newScript);
+	}
+
+/**
+ *
+ * Implements the RUN_TEST_STEP script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RunTestStep(const TDesC& aText)
+	{
+	// use TLex to decode the cmd line
+	TLex lex(aText);
+
+	// step over keyword
+	lex.NextToken();
+
+	// get guard timer
+	TPtrC timeout;
+	timeout.Set(lex.NextToken());
+
+	// get the other parameters
+	TPtrC suite, step, config, name, paramSet;
+	suite.Set(lex.NextToken());
+	step.Set(lex.NextToken());
+	config.Set(lex.NextToken());
+	name.Set(lex.NextToken());
+	if (name.Length()==0)
+		{
+		// name is optional, if not given use step 
+		name.Set(step);
+		}
+	paramSet.Set(lex.NextToken()); 
+	if (paramSet.Length()==0)
+		{
+		// paramSet is optional, if not given use name 
+		paramSet.Set(name);
+		}
+
+	// save the name of the current test suite / step
+	iCurrentSuiteName = suite;
+	iCurrentStepName = name;
+
+	TVerdict currentTestVerdict;
+
+	INFO_PRINTF2(_L("<a name=\"%S\"</a>"),&name);
+	
+	if (iMatchString.Length()>0 && name.Match(iMatchString)<0)
+		{
+		// we have a match string but no match - so skip
+		INFO_PRINTF2(_L("TEST_STEP:%S skipped"), &name);
+		iSkip = ETrue;
+		return;
+		}
+		
+	iSkip = EFalse;
+
+	// convert the guard timer value to a TInt64
+	TLex lexTimeOut(timeout);
+	TInt64 guardTimerValue;
+	if (lexTimeOut.Val(guardTimerValue) != KErrNone)
+		{
+		ERR_PRINTF2(_L("Error in guard timer value: %S"),
+					&timeout);
+		currentTestVerdict = EInconclusive;
+		}
+
+	else
+		{
+		// override guard timer if necessary
+		if((guardTimerValue == KNoGuardTimer) && (iGuardTimer != KNoGuardTimer))
+			{
+			INFO_PRINTF3(_L("Warning : Guard timer value overridden from %ld to %ld"),
+							guardTimerValue, iGuardTimer);
+			guardTimerValue = iGuardTimer;
+			}
+
+		// log the start of a test step
+		INFO_PRINTF7(_L("RUN_TEST_STEP:%S (step:%S suite:%S timeout:%ldmS config:%S(%S))"),
+					&name, &step, &suite, guardTimerValue, &config, &paramSet);
+
+		// NOTE. Now running multithreaded all the time.
+		currentTestVerdict = DoTestNewThread(suite, step, guardTimerValue, config, paramSet);
+		}
+
+	TPtrC verdictText = CLog::TestResultText(currentTestVerdict);
+	
+	INFO_PRINTF3(_L("TEST_STEP:%S returned:%S "), 
+				&name, &verdictText);
+
+	// this result is only significant if everything else has passed
+	if (iTestVerdict == EPass)
+		iTestVerdict = currentTestVerdict;
+
+	}
+
+/**
+ *
+ * Implements the RUN_PANIC_STEP script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RunPanicTestStep(const TDesC& aText)
+	{
+	// NOTE. RUN_PANIC_STEP now incorporates the panic reason and category
+
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// start at the begining
+	TPtrC timeout=lex.NextToken();
+
+	// step over the keyword
+	timeout.Set(lex.NextToken());
+
+	// get the other parameters
+	TPtrC suite, step;
+	TPtrC category, reason;
+	TPtrC config, name, paramSet;
+
+	suite.Set(lex.NextToken());
+	step.Set(lex.NextToken());
+	category.Set(lex.NextToken());
+	reason.Set(lex.NextToken());
+	config.Set(lex.NextToken());
+	name.Set(lex.NextToken());
+	if (name.Length()==0)
+		{
+		// name is optional, if not given use step 
+		name.Set(step);
+		}
+	paramSet.Set(lex.NextToken()); 
+	if (paramSet.Length()==0)
+		{
+		// paramSet is optional, if not given use name 
+		paramSet.Set(name);
+		}
+
+	if (iMatchString.Length()>0 && name.Match(iMatchString)<0)
+		{
+		// we have a match string but no match - so skip
+		INFO_PRINTF2(_L("TEST_STEP:%S skipped"), &name);
+		iSkip = ETrue;
+		return;
+		}
+		
+	iSkip = EFalse;
+
+	// save the name of the current test suite / step
+	iCurrentSuiteName = suite;
+	iCurrentStepName = name;
+
+	// convert the guard timer value to a TInt
+	TLex lexTimeOut(timeout);
+	TInt64 guardTimerValue;
+	if (lexTimeOut.Val(guardTimerValue) != KErrNone)
+		{
+		ERR_PRINTF3(_L("Error in guard timer value:%S using default %dmS"), 
+					&timeout, KPanicGuardTimerDefault);
+		guardTimerValue = KPanicGuardTimerDefault;
+		}
+
+	// convert the exitReason value to a TInt
+	TLex lexReason(reason);
+	TInt exitReason;
+	if (lexReason.Val(exitReason) != KErrNone)
+		{
+		ERR_PRINTF3(_L("Error in exitReason value:%S using default %d"), 
+					&reason, KPanicExitReasonDefault);
+		exitReason = KPanicExitReasonDefault;
+		}
+
+	// override guard timer if necessary
+	if((guardTimerValue == KNoGuardTimer) && (iGuardTimer != KNoGuardTimer))
+		{
+		INFO_PRINTF3(_L("Warning : Guard timer value overridden from %ld to %ld"),
+						guardTimerValue, iGuardTimer);
+		guardTimerValue = iGuardTimer;
+		}
+
+	// log the start of a test step
+	INFO_PRINTF9(_L("RUN_PANIC_STEP:%S (step:%S suite:%S timeout:%ldmS category:%S reason:%d config:%S(%S))"),
+				&name, &step, &suite, guardTimerValue, &category, exitReason, &config, &paramSet);
+
+	// run the test step
+	TVerdict currentTestVerdict;
+
+	// now running multithreaded all the time
+	currentTestVerdict = DoPanicTest(suite, step, guardTimerValue,
+									 category, exitReason, config, paramSet);
+
+	TPtrC verdictText = CLog::TestResultText(currentTestVerdict);
+	INFO_PRINTF3(_L("TEST_STEP:%S returned:%S "), 
+				&name, &verdictText);
+
+	// this result is only significant if every thing else has passed
+	if (iTestVerdict == EPass)
+		iTestVerdict = currentTestVerdict;
+
+	}
+	
+/**
+ *
+ * Implements the RUN_TERMINATION_STEP script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RunTerminationTestStep(const TDesC& aText)
+	{
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// start at the begining
+	TPtrC timeout=lex.NextToken();
+
+	// step over the keyword
+	timeout.Set(lex.NextToken());
+
+	// get the other parameters
+	TPtrC suite, step;
+	TPtrC reason;
+	TPtrC config;
+
+	suite.Set(lex.NextToken());
+	step.Set(lex.NextToken());
+	reason.Set(lex.NextToken());
+	config.Set(lex.NextToken());
+
+	// save the name of the current test suite / step
+	iCurrentSuiteName = suite;
+	iCurrentStepName = step;
+
+	// convert the guard timer value to a TInt
+	TLex lexTimeOut(timeout);
+	TInt64 guardTimerValue;
+	if (lexTimeOut.Val(guardTimerValue) != KErrNone)
+		{
+		ERR_PRINTF3(_L("Error in guard timer value:%S using default %dmS"), 
+					&timeout, KPanicGuardTimerDefault);
+		guardTimerValue = KPanicGuardTimerDefault;
+		}
+
+	// convert the exitReason value to a TInt
+	TLex lexReason(reason);
+	TInt exitReason;
+	if (lexReason.Val(exitReason) != KErrNone)
+		{
+		ERR_PRINTF3(_L("Error in exitReason value:%S using default %d"), 
+					&reason, KPanicExitReasonDefault);
+		exitReason = KPanicExitReasonDefault;
+		}
+
+	// override guard timer if necessary
+	if((guardTimerValue == KNoGuardTimer) && (iGuardTimer != KNoGuardTimer))
+	{
+		INFO_PRINTF3(_L("Warning : Guard timer value overridden from %ld to %ld"),
+						guardTimerValue, iGuardTimer);
+		guardTimerValue = iGuardTimer;
+	}
+
+	// log the start of a test step
+	INFO_PRINTF6(_L("RUN_TERMINATION_STEP:%S suite:%S timeout:%ldmS reason:%d config:%S"),
+				&step, &suite, guardTimerValue, exitReason, &config);
+
+	// run the test step
+	TVerdict currentTestVerdict;
+
+	// now running multithreaded all the time
+	currentTestVerdict = DoTerminationTest(suite, step, guardTimerValue,
+									 exitReason, config);
+
+	TPtrC verdictText = CLog::TestResultText(currentTestVerdict);
+	INFO_PRINTF3(_L("TEST_STEP:%S returned:%S "), 
+				&step, &verdictText);
+
+	// this result is only significant if every thing else has passed
+	if (iTestVerdict == EPass)
+		iTestVerdict = currentTestVerdict;
+
+	}
+
+/**
+ *
+ * Implements the RUN_UTILS script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RunUtil(const TDesC& aText)
+	{
+	// Call the utils
+	iTestUtils->RunUtils(aText);
+	}
+
+/**
+ *
+ * Implements the REBOOT script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::Reboot()
+	{
+	WARN_PRINTF1(_L("Warning : REBOOT command not implemented"));
+	}
+
+/**
+ *
+ * Static function to call DoTestStep which is run
+ * in a separate thread
+ *
+ * @param	"TAny* aPtr"
+ *			The test step data
+ *
+ * @return "TInt"
+ *			EPOC error code
+ *
+ * @xxxx
+ *
+ */
+TInt CParseLine::ThreadFunctionL(TAny* aPtr)
+	{
+	TInt result = KErrNone;
+
+	// get clean-up stack
+  	CTrapCleanup* trapCleanup = CTrapCleanup::New();
+
+	TRAPD(err, result = ThreadTrapFunctionL(aPtr));
+
+	delete trapCleanup;
+	return((err != KErrNone) ? err : result);
+	}
+
+/**
+ *
+ * Main function to call DoTestStep, called from within
+ * a trap
+ *
+ * @param	"TAny* aPtr"
+ *			The test step data
+ *
+ * @return "TInt"
+ *			EPOC error code
+ *
+ * @xxxx
+ *
+ */
+TInt CParseLine::ThreadTrapFunctionL(TAny* aPtr)
+	{
+	// get the data for the test
+	CStepData* data = REINTERPRET_CAST(CStepData*, aPtr);
+	CSuiteDll* suiteDll = data->SuiteDll();
+	CTestSuite* testSuite = suiteDll->Suite();
+
+	// setup local logger
+	CLog* logClient	= CLog::NewLC();
+	logClient->OpenLogFileL();
+	testSuite->SetLogSystem(logClient);
+
+	// do the test step 
+	TVerdict result =  testSuite->DoTestStep(data->Step(), data->Config(), data->ParamSet());
+
+	// NB it is the CALLING program's responsibility to save/restore the logger.
+	// If the thread terminates prematurely, the logger is in an undefined state.
+
+	CleanupStack::PopAndDestroy(logClient);
+	testSuite->SetLogSystem(NULL);
+
+	// return the test result
+	return result;
+	}
+
+/**
+ *
+ * Do a test step in a new thread.
+ *
+ * @param	"const TDesC& aSuite"
+ *			The test suite
+ *
+ * @param	"const TDesC& aStep"
+ *			The test step
+ *
+ * @param	"TInt aGuardTimerValue"
+ *			The guard timer value
+ *
+ * @param	"const TDesC& aConfig"
+ *			The config data
+ *
+ * @return  "TVerdict"
+ *			The test result
+ *
+ * @xxxx
+ *
+ */
+TVerdict CParseLine::DoTestNewThread(const TDesC& aSuite, const TDesC& aStep, 
+							TInt64 aGuardTimerValue, const TDesC& aConfig, const TDesC& aParamSet)
+	{
+	//	get the number of suites loaded
+	TInt noOfDlls = iArrayLoadedSuiteDll->Count();
+
+	// search the list of loaded test suite DLLs for the required one
+	for (TInt i = 0; i < noOfDlls; i++)
+		{
+		CSuiteDll* ptrSuite = iArrayLoadedSuiteDll->At(i);
+		TPtrC name = ptrSuite->Name();
+
+		if (name.FindF(aSuite)!= KErrNotFound)
+			{
+			// reset step status
+			CTestSuite* testSuite = ptrSuite->Suite();
+			testSuite->SetStepStatus(EStepStatusNone);
+
+			// store old log status, for restore at thread exit
+			// NB we must do this here, as if thread times out, the log
+			// is in an undefined state
+			CLog* oldLogger = testSuite->LogSystem();
+
+			CStepData* data = NULL;
+			TRAPD(err, data = CStepData::NewL(aStep, aConfig, aParamSet, ptrSuite));
+			if (err != KErrNone)
+				{
+				ERR_PRINTF2(_L("CStepData::NewL() left with error %d : unable to create test data!"), err);
+				return EFail;
+				}
+			
+			// get step's own stack and heap sizes
+			TInt theHeapSize = KMaxTestThreadHeapSize;
+			TInt theStackSize = KTestStackSize;
+			GetHeapAndStackSize(data, &theHeapSize, &theStackSize);
+
+			TInt res = KErrAlreadyExists;
+			RThread newThread;
+
+			TPtrC threadBaseName(_L("DoTestThread"));
+			TBuf<32> threadName;
+			
+			// create a unique named test thread
+			// this will leave if creation is not successful
+			TRAP (res, CreateUniqueTestThreadL( threadBaseName, 
+												threadName, 
+												newThread, 
+												ThreadFunctionL,
+												theStackSize,
+												KMinHeapSize,
+												theHeapSize,
+												data ) );
+
+			
+			if (res != KErrNone)
+				{
+				ERR_PRINTF2(_L("CreateUniqueTestThreadL() left with error %d : unable to create test thread "), res);
+				delete data;
+				data = NULL;
+				return EFail;
+				}
+
+			// start clock
+			TTime testStart, testStop;
+			testStart.HomeTime();
+			
+			// start the thread and request the status
+			TRequestStatus threadStatus;
+			newThread.Logon(threadStatus);
+
+			// if there is no guard timer value, don't time at all
+			if (aGuardTimerValue == KNoGuardTimer)
+				{
+				// no guard timer
+				newThread.Resume();
+				User::WaitForRequest(threadStatus);
+				}
+			else
+				{
+				// wait for either test thread or timer to end
+				RTimer guardTimer;
+				guardTimer.CreateLocal();			// create for this thread
+				TRequestStatus timerStatus;
+				newThread.Resume();
+
+				// NB now using At() to allow 64-bit timer values
+				TInt64 guardTimerUsec = aGuardTimerValue * 1000;
+				TInt64 totalTime = 0;
+
+				for (;;)
+					{
+					if (totalTime>=guardTimerUsec) // timeout has occured
+						break;
+					TInt timeout;
+
+					if (totalTime+KTimeIncrement >= guardTimerUsec)
+						{
+						TInt64 temp = guardTimerUsec-totalTime;
+						timeout = I64INT(temp);
+						}
+					else
+						timeout = KTimeIncrement;
+					totalTime += timeout;
+					guardTimer.After(timerStatus, timeout);
+					User::WaitForRequest(threadStatus, timerStatus);
+					if (threadStatus!=KRequestPending) // normal exit
+						break;
+					}
+
+				guardTimer.Cancel();
+				guardTimer.Close();
+				}
+
+			// reset any file server error simulations
+			RFs fs;
+			TInt fsError = fs.Connect();
+			if (fsError == KErrNone)
+				{
+				fs.SetErrorCondition(KErrNone);
+				}
+			fs.Close();
+
+			// restore logger
+			testSuite->SetLogSystem(oldLogger);
+
+			// get the test result
+			TVerdict result = STATIC_CAST(TVerdict, threadStatus.Int());
+
+			// check terminated ok
+			switch(newThread.ExitType())
+				{
+				case EExitTerminate:
+				case EExitKill:
+					break;
+				case EExitPanic:
+					{
+					TExitCategoryName exitCategory = newThread.ExitCategory();
+					TInt exitReason = newThread.ExitReason();
+ 					ERR_PRINTF3(_L("Thread had a panic %S:%d"), &exitCategory, exitReason);
+
+					result = EFail;
+					}
+					break;
+				case EExitPending:
+					// if the thread is still pending then the guard timer must have expired
+					ERR_PRINTF1(_L("Thread timed out"));
+					// kill the test step thread
+					newThread.Kill(1);
+					// give the OS time to cleanup devices, etc.
+					// NB if the thread dies, the postamble will NOT run
+					User::After(2000000);
+					result = EFail;
+					break;
+				default:
+					break;
+				}
+
+			// done with the test thread
+			newThread.Close();
+
+			// stop clock
+			testStop.HomeTime();
+
+			TUint testDuration = I64INT(testStop.MicroSecondsFrom(testStart).Int64());
+			testDuration /= 1000; // to microseconds
+			TUint testDurationMsec = testDuration % 1000;
+			TUint testDurationSec = testDuration / 1000;
+			INFO_PRINTF3(_L("Test took %d.%03d sec"), testDurationSec, testDurationMsec);
+
+			// return the test verdict
+			delete data;
+			data = NULL;
+			return result;
+			}
+		}
+
+	// the required suite has not been found
+	ERR_PRINTF3(_L("Error in test step:%S - cannot find suite:%S" ),
+					&aStep, &aSuite);
+
+	return ETestSuiteError;
+	}
+
+/**
+ *
+ * Do a test step which is expected to panic.
+ *
+ * @param	"const TDesC& aSuite"
+ *			The test suite
+ *
+ * @param	"const TDesC& aStep"
+ *			The test step
+ *
+ * @param	"TInt aGuardTimerValue"
+ *			The guard timer value
+ *
+ * @param	"const TExitCategoryName aExitCategory"
+ *			The expected exit category
+ *
+ * @param	"TInt aExitReason"
+ *			The expected exit reason
+ *
+ * @param	"const TDesC& aConfig"
+ *			The config data
+ *
+ * @return "TVerdict"
+ *			The test result
+ *
+ * @xxxx
+ *
+ */
+TVerdict CParseLine::DoPanicTest(const TDesC& aSuite, const TDesC& aStep, TInt64 aGuardTimerValue,
+									  const TExitCategoryName aExitCategory, TInt aExitReason, 
+									  const TDesC& aConfig, const TDesC& aParamSet)
+	{
+
+	//	get the number of suites loaded
+	TInt noOfDlls = iArrayLoadedSuiteDll->Count();
+
+	// search the list of loaded test suite DLLs for the required one
+	for (TInt i = 0; i < noOfDlls; i++)
+		{
+		CSuiteDll* ptrSuite = iArrayLoadedSuiteDll->At(i);
+		TPtrC name = ptrSuite->Name();
+
+		if (name.FindF(aSuite)!= KErrNotFound)
+			{
+			// reset step status
+			CTestSuite* testSuite = ptrSuite->Suite();
+			testSuite->SetStepStatus(EStepStatusNone);
+
+			// store old log status, for restore at thread exit
+			// NB we must do this here, as if thread times out, the log
+			// is in an undefined state
+			CLog* oldLogger = testSuite->LogSystem();
+
+			CStepData* data = NULL;
+			TRAPD(err, data = CStepData::NewL(aStep, aConfig, aParamSet, ptrSuite));
+			if (err != KErrNone)
+				{
+				ERR_PRINTF2(_L("CStepData::NewL() left with error %d : unable to create test data!"), err);
+				return EFail;
+				}
+
+			// get step's own stack and heap sizes
+			TInt theHeapSize = KMaxTestThreadHeapSize;
+			TInt theStackSize = KTestStackSize;
+			GetHeapAndStackSize(data, &theHeapSize, &theStackSize);
+
+			TInt res = KErrAlreadyExists;
+			RThread newThread;
+
+			// create a unique test name by appending a counter
+			TPtrC threadBaseName(_L("DoTestThread"));
+			TBuf<32> threadName;
+			
+			// create a unique named test thread
+			// this will leave if creation is not successful
+			TRAP (res, CreateUniqueTestThreadL( threadBaseName, 
+												threadName, 
+												newThread, 
+												ThreadFunctionL,
+												theStackSize,
+												KMinHeapSize,
+												theHeapSize,
+												data ) );
+			
+			if (res != KErrNone)
+				{
+				ERR_PRINTF2(_L("CreateUniqueTestThreadL() left with error %d : unable to create test thread "), res);
+				delete data;
+				data = NULL;
+				return EFail;
+				}
+
+			// start clock
+			TTime testStart, testStop;
+			testStart.HomeTime();
+			
+			// start the thread and request the status
+			TRequestStatus threadStatus;
+			newThread.Logon(threadStatus);
+
+			// if there is no guard timer value, don't time at all
+			if (aGuardTimerValue == KNoGuardTimer)
+				{
+				// no guard timer
+				newThread.Resume();
+				User::WaitForRequest(threadStatus);
+				}
+			else
+				{
+				// wait for either test thread or timer to end
+				RTimer guardTimer;
+				guardTimer.CreateLocal();			// create for this thread
+				TRequestStatus timerStatus;
+				newThread.Resume();
+
+				// NB now using At() to allow 64-bit timer values
+				TInt64 guardTimerUsec = aGuardTimerValue * 1000;
+				TInt64 totalTime = 0;
+
+				for (;;)
+					{
+					if (totalTime>=guardTimerUsec) // timeout has occured
+						break;
+					TInt timeout;
+
+					if (totalTime+KTimeIncrement >= guardTimerUsec)
+						{
+						TInt64 temp = guardTimerUsec-totalTime;
+						timeout = I64INT(temp);
+						}
+					else
+						timeout = KTimeIncrement;
+						totalTime += timeout;
+					guardTimer.After(timerStatus, timeout);
+					User::WaitForRequest(threadStatus, timerStatus);
+					if (threadStatus!=KRequestPending) // normal exit
+						break;
+					}
+
+				guardTimer.Cancel();
+				guardTimer.Close();
+				}
+
+			// restore logger
+			testSuite->SetLogSystem(oldLogger);
+
+			// get the test result
+			TVerdict result = STATIC_CAST(TVerdict, threadStatus.Int());
+
+			// check terminated ok
+			switch(newThread.ExitType())
+				{
+				case EExitPanic:
+					{
+					TExitCategoryName exitCategory = newThread.ExitCategory();
+					TInt exitReason = newThread.ExitReason();
+					if((exitCategory != aExitCategory) || (exitReason != aExitReason && aExitReason != KNoPanicReason) )
+						{
+						ERR_PRINTF3(_L("Test step had an unexpected panic %S:%d and failed"),
+									&exitCategory, exitReason);
+						result = EFail;
+						}
+					else
+						{
+						// check here that the panic occurred within the test itself
+						CTestSuite* testSuite = ptrSuite->Suite();
+						TTestStepStatus status = testSuite->StepStatus();
+						switch(status)
+							{
+							case EStepStatusPreamble:
+								{
+								// thread panicked in the test itself - success
+								INFO_PRINTF3(_L("Test step had a panic %S:%d and passed"),
+											&exitCategory, exitReason);
+								result = EPass;
+								}
+								break;
+							case EStepStatusStart:
+								{
+								// thread panicked in preamble
+								ERR_PRINTF3(_L("Test step had a panic %S:%d in preamble"),
+											&exitCategory, exitReason);
+								result = EFail;
+								}
+								break;
+							case EStepStatusTest:
+								{
+								// thread panicked in postamble
+								ERR_PRINTF3(_L("Test step had a panic %S:%d in postamble"),
+											&exitCategory, exitReason);
+								result = EFail;
+								}
+								break;
+							default:
+								{
+								// thread panicked outside the test
+								ERR_PRINTF3(_L("Test step had a panic %S:%d outside the test"),
+											&exitCategory, exitReason);
+								result = EFail;
+								}
+								break;
+							}	// end switch
+						}
+					}
+					break;
+				case EExitPending:
+					// if the thread is still pending then the guard timer must have expired
+					ERR_PRINTF1(_L("Thread timed out"));
+					// kill the test step thread
+					newThread.Kill(1);
+					// give the OS time to cleanup devices, etc.
+					// NB if the thread dies, the postamble will NOT run
+					User::After(2000000);
+					result = EFail;
+					break;
+				case EExitTerminate:
+				case EExitKill:
+				default:
+					ERR_PRINTF1(_L("Test did not panic, so failed"));
+					result = EFail;
+					break;
+				}
+
+			// done with the test thread
+			newThread.Close();
+
+			// stop clock
+			testStop.HomeTime();
+
+			TUint testDuration = I64INT(testStop.MicroSecondsFrom(testStart).Int64());
+			testDuration /= 1000; // to microseconds
+			TUint testDurationMsec = testDuration % 1000;
+			TUint testDurationSec = testDuration / 1000;
+			INFO_PRINTF3(_L("Test took %d.%03d sec"), testDurationSec, testDurationMsec);
+
+			// return the test verdict
+			delete data;
+			data = NULL;
+			return result;
+			}
+		}
+
+	// the required suite has not been found
+	ERR_PRINTF3(_L("Error in test step:%S - cannot find suite:%S"),
+				&aStep, &aSuite );
+
+	return ETestSuiteError;
+	}
+	
+/**
+ *
+ * Do a test step which is expected to terminate.
+ *
+ * @param	"const TDesC& aSuite"
+ *			The test suite
+ *
+ * @param	"const TDesC& aStep"
+ *			The test step
+ *
+ * @param	"TInt aGuardTimerValue"
+ *			The guard timer value
+ *
+ * @param	"TInt aExitReason"
+ *			The expected exit reason
+ *
+ * @param	"const TDesC& aConfig"
+ *			The config data
+ *
+ * @return "TVerdict"
+ *			The test result
+ *
+ * @xxxx
+ *
+ */
+TVerdict CParseLine::DoTerminationTest(const TDesC& aSuite, const TDesC& aStep, TInt64 aGuardTimerValue,
+									  TInt aExitReason, const TDesC& aConfig)
+	{
+
+	//	get the number of suites loaded
+	TInt noOfDlls = iArrayLoadedSuiteDll->Count();
+
+	// search the list of loaded test suite DLLs for the required one
+	for (TInt i = 0; i < noOfDlls; i++)
+		{
+		CSuiteDll* ptrSuite = iArrayLoadedSuiteDll->At(i);
+		TPtrC name = ptrSuite->Name();
+
+		if (name.FindF(aSuite)!= KErrNotFound)
+			{
+			// reset step status
+			CTestSuite* testSuite = ptrSuite->Suite();
+			testSuite->SetStepStatus(EStepStatusNone);
+
+			// store old log status, for restore at thread exit
+			// NB we must do this here, as if thread times out, the log
+			// is in an undefined state
+			CLog* oldLogger = testSuite->LogSystem();
+
+			CStepData* data = NULL;
+			TRAPD(err, data = CStepData::NewL(aStep, aConfig, ptrSuite));
+			if (err != KErrNone)
+				{
+				ERR_PRINTF2(_L("CStepData::NewL() left with error %d : unable to create test data!"), err);
+				return EFail;
+				}
+
+			// get step's own stack and heap sizes
+			TInt theHeapSize = KMaxTestThreadHeapSize;
+			TInt theStackSize = KTestStackSize;
+			GetHeapAndStackSize(data, &theHeapSize, &theStackSize);
+
+			TInt res = KErrAlreadyExists;
+			RThread newThread;
+
+			// create a unique test name by appending a counter
+			TPtrC threadBaseName(_L("DoTestThread"));
+			TBuf<32> threadName;
+			
+			// create a unique named test thread
+			// this will leave if creation is not successful
+			TRAP (res, CreateUniqueTestThreadL( threadBaseName, 
+												threadName, 
+												newThread, 
+												ThreadFunctionL,
+												theStackSize,
+												KMinHeapSize,
+												theHeapSize,
+												data ) );
+			
+			if (res != KErrNone)
+				{
+				ERR_PRINTF2(_L("CreateUniqueTestThreadL() left with error %d : unable to create test thread "), res);
+				delete data;
+				data = NULL;
+				return EFail;
+				}
+
+			// start clock
+			TTime testStart, testStop;
+			testStart.HomeTime();
+			
+			// start the thread and request the status
+			TRequestStatus threadStatus;
+			newThread.Logon(threadStatus);
+
+			// if there is no guard timer value, don't time at all
+			if (aGuardTimerValue == KNoGuardTimer)
+				{
+				// no guard timer
+				newThread.Resume();
+				User::WaitForRequest(threadStatus);
+				}
+			else
+				{
+				// wait for either test thread or timer to end
+				RTimer guardTimer;
+				guardTimer.CreateLocal();			// create for this thread
+				TRequestStatus timerStatus;
+				newThread.Resume();
+
+				// NB now using At() to allow 64-bit timer values
+				TInt64 guardTimerUsec = aGuardTimerValue * 1000;
+				TInt64 totalTime = 0;
+
+				for (;;)
+					{
+					if (totalTime>=guardTimerUsec) // timeout has occured
+						break;
+					TInt timeout;
+
+					if (totalTime+KTimeIncrement >= guardTimerUsec)
+						{
+						TInt64 temp = guardTimerUsec-totalTime;
+						timeout = I64INT(temp);
+						}
+					else
+						timeout = KTimeIncrement;
+						totalTime += timeout;
+					guardTimer.After(timerStatus, timeout);
+					User::WaitForRequest(threadStatus, timerStatus);
+					if (threadStatus!=KRequestPending) // normal exit
+						break;
+					}
+
+				guardTimer.Cancel();
+				guardTimer.Close();
+				}
+
+			// restore logger
+			testSuite->SetLogSystem(oldLogger);
+
+			// get the test result
+			TVerdict result = STATIC_CAST(TVerdict, threadStatus.Int());
+
+			// check terminated ok
+			switch(newThread.ExitType())
+				{
+				case EExitTerminate:
+				case EExitKill:
+					{
+					TInt exitReason = newThread.ExitReason();
+					if(exitReason != aExitReason)
+						{
+						ERR_PRINTF2(_L("Test step had an unexpected exit reason:%d and failed"),
+									exitReason);
+						result = EFail;
+						}
+					else
+						{
+						// check here that the panic occurred within the test itself
+						CTestSuite* testSuite = ptrSuite->Suite();
+						TTestStepStatus status = testSuite->StepStatus();
+						switch(status)
+							{
+							case EStepStatusPreamble:
+								{
+								// thread terminated in the test itself - success
+								INFO_PRINTF2(_L("Test step had terminated:%d and passed"),
+											exitReason);
+								result = EPass;
+								}
+								break;
+							case EStepStatusStart:
+								{
+								// thread panicked in preamble
+								ERR_PRINTF2(_L("Test step had terminated:%d in preamble"),
+											exitReason);
+								result = EFail;
+								}
+								break;
+							case EStepStatusTest:
+								{
+								// thread panicked in postamble
+								ERR_PRINTF2(_L("Test step had terminated:%d in postamble"),
+											exitReason);
+								result = EFail;
+								}
+								break;
+							default:
+								{
+								// thread panicked outside the test
+								ERR_PRINTF2(_L("Test step had terminated:%d outside the test"),
+											exitReason);
+								result = EFail;
+								}
+								break;
+							}	// end switch
+						}
+					}
+					break;
+				case EExitPending:
+					// if the thread is still pending then the guard timer must have expired
+					ERR_PRINTF1(_L("Thread timed out"));
+					// kill the test step thread
+					newThread.Kill(1);
+					// give the OS time to cleanup devices, etc.
+					// NB if the thread dies, the postamble will NOT run
+					User::After(2000000);
+					result = EFail;
+					break;
+				case EExitPanic:
+				default:
+					ERR_PRINTF1(_L("Test did not terminate, so failed"));
+					result = EFail;
+					break;
+				}
+
+			// done with the test thread
+			newThread.Close();
+
+			// stop clock
+			testStop.HomeTime();
+
+			TUint testDuration = I64INT(testStop.MicroSecondsFrom(testStart).Int64());
+			testDuration /= 1000; // to microseconds
+			TUint testDurationMsec = testDuration % 1000;
+			TUint testDurationSec = testDuration / 1000;
+			INFO_PRINTF3(_L("Test took %d.%03d sec"), testDurationSec, testDurationMsec);
+
+			// return the test verdict
+			delete data;
+			data = NULL;
+			return result;
+			}
+		}
+
+	// the required suite has not been found
+	ERR_PRINTF3(_L("Error in test step:%S - cannot find suite:%S"),
+				&aStep, &aSuite );
+
+	return ETestSuiteError;
+	}
+
+/**
+ *
+ * Gets a step's heap and stack size.
+ *
+ * @param	"const CStepData& aStepData"
+ *			The step data
+ * @param	"TInt* aHeapSize"
+ *			Returns the step's heap size
+ * @param	"TInt* aStackSize"
+ *			Returns the step's stack size
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::GetHeapAndStackSize(const CStepData* aStepData, TInt* aHeapSize, TInt* aStackSize)
+	{
+	CSuiteDll* suiteDll = aStepData->SuiteDll();
+	CTestSuite* testSuite = suiteDll->Suite();
+	testSuite->GetHeapAndStackSize(aStepData->Step(), aHeapSize, aStackSize);
+	}
+
+/**
+ *
+ * Implements the RUN_PROGRAM script command.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RunProgram(const TDesC& aText)
+	{
+	TPtrC param;
+
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// step over the keyword
+	lex.NextToken();
+
+	// get program name	
+	TPtrC token;
+	token.Set(lex.NextToken());
+
+	// get the parameters
+	param.Set(lex.NextToken());
+
+	INFO_PRINTF1(_L("Run Program "));
+
+
+	// In the ARM build run program as a new process
+	// use the rest of the text as parameters
+	RProcess program;
+	TInt ret = program.Create(token, lex.Remainder());
+
+	if (ret != KErrNone)
+		{
+		TPtrC errortxt = CLog::EpocErrorToText(ret);
+		ERR_PRINTF2(_L("Failed to start process - error %S"), &errortxt);
+		return;
+		}
+	else
+		{
+		INFO_PRINTF1(_L("Program started"));
+
+		// start program
+		TRequestStatus threadStatus;
+		program.Logon(threadStatus);
+		program.Resume();
+
+		// wait for guard timer
+		User::WaitForRequest(threadStatus);
+
+		// check return type
+		if (program.ExitType() == EExitPanic)
+			INFO_PRINTF1(_L("Program returned EExitPanic"));
+		else if (program.ExitType() == EExitPending)
+			INFO_PRINTF1(_L("Program returned EExitPending"));
+		else
+			INFO_PRINTF1(_L("Program returned EExitTerminate"));
+		}
+	}
+
+/**
+ *
+ * Implements the LOG_SETTINGS script command.
+ * Command format is LOG_SETTINGS "put src." (1/0),
+ * "HTML format" (1/0)
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::LogSettings(const TDesC& aText)
+	{
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// start at the begining
+	TPtrC token=lex.NextToken();
+
+	// step over the keyword
+	//Get information about source
+	token.Set(lex.NextToken());
+
+	TLex srcLex(token);
+	TInt isSrc = ETrue; //Shall we put src information?
+	if (srcLex.Val(isSrc) != KErrNone)
+		{
+		ERR_PRINTF2(_L("Error in LOG_SETTINGS: could not decode >%S< as value(0/1)"),
+					&token);
+		}
+	else
+		{
+		iLog->SetPutSrcInfo(isSrc) ;
+		}
+	//Get information about format
+	token.Set(lex.NextToken());
+	TLex htmlLex(token);
+
+	if (htmlLex.Val(isSrc) != KErrNone)
+		{
+		ERR_PRINTF2(_L("Error in LOG_SETTINGS: could not decode >%S< as value(0/1)"),
+					&token);
+		}
+	else
+		{
+		iLog->SetHtmlLogMode(isSrc);
+		}
+	}
+
+
+/**
+ *
+ * Implements the LOAD_SUITE script command.
+ * This function loads a required test suite DLL
+ * It also creates a CTestSuite object as a record
+ * of the loaded DLL.
+ *
+ * @param	"const TDesC& aText"
+ *			The script line
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::LoadSuiteL(const TDesC& aText)
+	{
+	// use Tlex to decode the cmd line
+	TLex lex(aText);
+
+	// step over the keyword
+	lex.NextToken();
+
+	// get suite name
+	TPtrC token;
+	token.Set(lex.NextToken());
+
+	// check not already loaded
+	// by searching the list of loaded test suite DLLs for the required one
+	// start with the number of suites loaded
+	TInt noOfDlls = iArrayLoadedSuiteDll->Count();
+	for (TInt i = 0; i < noOfDlls; i++)
+		{
+		CSuiteDll* ptrSuite = iArrayLoadedSuiteDll->At(i);
+		TPtrC name = ptrSuite->Name();
+
+		// check the names
+		if (name.FindF(token) != KErrNotFound)
+			{
+			// this suite DLL is already loaded
+	 		WARN_PRINTF2(_L("Warning: Test suite %S already loaded - not re-loaded"), &token);
+			return;
+			}
+		}
+
+
+	// create a new CSuiteDll object to store info on loaded DLL
+	CSuiteDll* newRef = NULL;
+
+	newRef = CSuiteDll::NewL(token, iLog);
+
+	CTestSuite* testSuite = newRef->Suite();
+
+	// set default severity and logging system
+	testSuite->SetSeverity(iSeverity);
+	testSuite->SetLogSystem(iLog);
+	
+	// add to data
+	iArrayLoadedSuiteDll->AppendL(newRef);
+	}
+
+
+/**
+ *
+ * Unload all the loaded DLLs
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::Unload()
+	{
+	if (iArrayLoadedSuiteDll)
+		{
+		// unload all the loaded DLLS and their records
+		iArrayLoadedSuiteDll->ResetAndDestroy();
+		}
+	}
+
+/**
+ *
+ * Mark the heap
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::HeapMark()
+	{
+	ERR_PRINTF1(_L("Warning: Command HEAP_MARK no longer supported. Heap marking/checking should be done within test code"));
+
+	// __UHEAP_MARK;
+	}
+
+
+/**
+ *
+ * Check the heap
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::HeapCheck()
+	{
+	ERR_PRINTF1(_L("Warning: Command HEAP_CHECK no longer supported. Heap marking/checking should be done within test code"));
+
+	// __UHEAP_MARKEND;
+	}
+
+/**
+ *
+ * Mark request
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RequestMark()
+	{
+	// get number of outstanding requetsts on thread before we run the test
+	iReqsAtStart = RThread().RequestCount();
+	INFO_PRINTF2(_L("Requests at the start %d "),iReqsAtStart);
+	}
+
+
+/**
+ *
+ * Check request
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::RequestCheck()
+	{
+	// check the number of outstanding requests against recorded value
+	INFO_PRINTF3(_L("Requests at the start %d now %d"),
+				iReqsAtStart, RThread().RequestCount());
+
+	if (iReqsAtStart != RThread().RequestCount())
+		{
+		ERR_PRINTF1(_L("Test failed on requests count"));
+
+		// this result is only significant if every thing else has passed
+		if (iTestVerdict == EPass)
+			iTestVerdict = EFail;
+
+		}
+	}
+
+
+/**
+ *
+ * Mark number of handles
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::HandlesMark()
+	{
+	// get number of Handles *before* we start the program
+	RThread().HandleCount(iProcessHandleCountBefore, iThreadHandleCountBefore);
+
+	INFO_PRINTF3(_L("HandlesMark : process handle count %d thread handle count %d"),
+				iProcessHandleCountBefore,
+				iThreadHandleCountBefore);
+	}
+
+/**
+ *
+ * Check number of handles
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::HandlesCheck()
+	{
+	TInt processHandleCountAfter;
+	TInt threadHandleCountAfter;
+	RThread().HandleCount(processHandleCountAfter, threadHandleCountAfter);
+
+	INFO_PRINTF3(_L("HandlesCheck : process handle count %d thread handle count %d"),
+				processHandleCountAfter,
+				threadHandleCountAfter);
+
+	// check that we are closing all the threads
+	if(iThreadHandleCountBefore != threadHandleCountAfter)
+		{
+		ERR_PRINTF1(_L("Test failed on thread handle count"));
+
+		// this result is only significant if everything else has passed
+		if (iTestVerdict == EPass)
+			iTestVerdict = EFail;
+		}
+
+	// check that we are closing all the handles
+	if(iProcessHandleCountBefore != processHandleCountAfter)
+		{
+		ERR_PRINTF1(_L("Test failed on process handle count"));
+
+		// this result is only significant if everything else has passed
+		if (iTestVerdict == EPass)
+			iTestVerdict = EFail;
+		}
+	}
+
+/**
+ *
+ * Traceable logging function for parseline.
+ * To be called only with macros
+ *
+ * @param	"const TText8* aFile"
+ *			Source code file name
+ *
+ * @param	"TInt aLine"
+ *			Source code line
+ *
+ * @param	"TInt aSeverity"
+ *			Severity level required to log
+ *
+ * @param	"TRefByValue<const TDesC16> aFmt"
+ *			Printf-style format.
+ *
+ * @param	"..."
+ *			Variable print parameters
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::LogExtra(const TText8* aFile, TInt aLine, TInt aSeverity,
+		TRefByValue<const TDesC16> aFmt,...)
+	{
+	VA_LIST aList;
+	VA_START(aList, aFmt);
+
+	if(LogSeverity::IsActive(aSeverity, iSeverity))
+		{
+		if(iLog)
+			{
+			iLog->LogExtra(aFile, aLine, aSeverity, aFmt, aList);
+			}
+		}
+
+	VA_END(aList);
+	}
+
+/**
+ *
+ * Get current suite name.
+ *
+ * @return "TPtrC"
+ *			The suite name
+ *
+ * @xxxx
+ *
+ */
+TPtrC CParseLine::CurrentSuiteName() const
+	{
+	return iCurrentSuiteName;
+	}
+
+/**
+ *
+ * Get current step name.
+ *
+ * @return "TPtrC"
+ *			The step name
+ *
+ * @xxxx
+ *
+ */
+TPtrC CParseLine::CurrentStepName() const
+	{
+	return iCurrentStepName;
+	}
+
+/**
+ *
+ * Create a thread with a unique name from a base thread name
+ * e.g. "TestThread" may become "TestThread00000002"
+ * This test will leave instantly if an error other than 
+ * KErrAlreadyExists occurs. 
+ *
+ *
+ * @param	"TDesC& aBaseName"
+ *			The base name to use.  This will be modified to contain
+ *			the new unique thread name if creation is successful.
+ *
+ * @param	"TDes& aThreadName"
+ *			The thread name to use.  This will be modified to contain
+ *			the new unique thread name if creation is successful.  This must
+ *			be non NULL.
+ *
+ * @param	"RThread&"
+ *			An RThread which will be created.  This must not be a valid handle.
+ *
+ * @param	"TThreadFunction aFunction"
+ *			Thread function to use in RThread.
+ *
+ * @param	"TInt aStackSize"
+ *			The size of the new thread's stack.
+ *
+ * @param	"TInt aHeapMinSize"
+ *			The minimum size for the new thread's heap.
+ *
+ * @param	"TInt aHeapMaxSize"
+ *			The maximum size for the new thread's heap.
+ *
+ * @param	"TAny *aPtr"
+ *			Data to pass to the new thread.
+ *
+ * @leave	"function will leave with an error code if a thread cannot
+ *			be created after KMaxThreadAttempts tries."
+ *
+ * @xxxx
+ *
+ */
+void CParseLine::CreateUniqueTestThreadL(const TDesC& aBaseName, TDes& aThreadName, RThread& aTestThread, TThreadFunction aFunction, TInt aStackSize, TInt aHeapMinSize, TInt aHeapMaxSize, TAny *aPtr)
+	{	
+	TInt res = KErrAlreadyExists;
+	
+	// attempt to create a thread with the name aBaseName + counter.
+	for (TUint i = 0; i < KMaxThreadAttempts; i++)
+		{
+		// copy the base thread name
+		aThreadName.Copy(aBaseName);
+		
+		// append the current counter to the threadname
+		aThreadName.AppendNumFixedWidth(i, EDecimal, 8);
+			
+		// run in a new thread, with a new heap		
+		res = aTestThread.Create(aThreadName,
+				aFunction,
+				aStackSize,
+				aHeapMinSize,
+				aHeapMaxSize,
+				aPtr);
+					
+		// if thread created successfully then we have
+		// a unique threadname else if an error code other
+		// than KErrAlreadyExists occurs then exit immediately.
+		if ((res == KErrNone) || (res != KErrAlreadyExists)) 
+			break;
+		}
+		
+	User::LeaveIfError(res);
+	}
+
+/**
+ *
+ * Static constructor for CSuiteDll.
+ *
+ *
+ * @return	"CSuiteDll*"
+ *			The constructed CSuiteDll
+ *
+ * @xxxx
+ *
+ */
+CSuiteDll* CSuiteDll::NewL(const TDesC& aName, CLog* aLog)
+	{
+	CSuiteDll* self = new(ELeave) CSuiteDll;
+	CleanupStack::PushL(self);
+	self->ConstructL(aName, aLog);
+	CleanupStack::Pop();
+	return self;
+	}
+
+/**
+ *
+ * CSuiteDLL second-phase constructor
+ * Loads a test suite dll and saves the name and test
+ * suite pointers.
+ *
+ * @param "TDesC& aName"
+ *			The test suite name
+ *
+ * @param "CLog* aLog"
+ *			The logger to use
+ *
+ * @xxxx
+ *
+ */
+void CSuiteDll::ConstructL(const TDesC& aName, CLog* aLog)
+	{
+	iLog = aLog;
+
+	User::Check();
+	// load DLL by name
+	TInt ret = iLibrary.Load(aName, KTxtDLLpath);
+
+	User::Check();
+
+	if (ret == KErrNotFound)
+		{
+		iLog->LogExtra(__FILE8__, __LINE__, ESevrErr, _L("Test suite %S was not found. Check any other DLLs required by %S"), &aName, &aName);
+		User::Leave(ret);
+		}
+	else if (ret != KErrNone)
+		{
+		iLog->LogExtra(__FILE8__, __LINE__, ESevrErr, _L("Test suite %S found but would not load. Check any other DLLs required by %S"), &aName, &aName);
+		User::Leave(ret);
+		}
+
+	// save the name
+	iName.Copy(aName);
+
+	// get the interface pointer at ordinal 1
+	const TInt KLibraryOrdinal = 1;
+	TLibraryFunction  entryL = iLibrary.Lookup(KLibraryOrdinal);
+
+    // Call this interface pointer to create new CTestSuite
+	// If this call goes to the wrong function then the test
+	// suite does not have the correct function at ordinal 1.
+	// This is usually caused by an error in the def file.
+    iTestSuite = REINTERPRET_CAST(CTestSuite*, entryL());
+
+    // NB :- Second-phase constructor for CTestSuite has already been called in entryL() above.
+	// There is no need to call it again!
+
+	// set suite severity level
+	iTestSuite->SetSeverity(iLog->Severity());
+
+	// get the version information
+	TPtrC versiontxt = iTestSuite->GetVersion();
+
+	// add to log
+	iLog->LogExtra(__FILE8__, __LINE__, ESevrInfo, _L("LOAD_SUITE %S version %S loaded ok"),&aName, &versiontxt );
+	}
+
+/**
+ *
+ * CSuiteDLL destructor
+ *
+ * @xxxx
+ *
+ */
+CSuiteDll::~CSuiteDll()
+	{
+	// delete the TestSuiteObject in the loaded DLL
+	delete iTestSuite;
+
+	// close and unload the library
+	iLibrary.Close();
+	}
+
+/**
+ *
+ * CSuiteDLL accessor : suite
+ *
+ * @return "CTestSuite*"
+ *			The test suite.
+ *
+ * @xxxx
+ *
+ */
+CTestSuite* CSuiteDll::Suite() const
+	{
+	return iTestSuite;
+	}
+
+/**
+ *
+ * CSuiteDLL accessor : suite name
+ *
+ * @return "TPtrC"
+ *			The suite name.
+ *
+ * @xxxx
+ *
+ */
+TPtrC CSuiteDll::Name() const
+	{
+	return iName;
+	}
+
+/**
+ *
+ * CStepData
+ *
+ * @xxxx
+ *
+ */
+CStepData* CStepData::NewL(const TDesC& aStep, const TDesC& aConfig, CSuiteDll* aSuite)
+	{
+	return NewL(aStep, aConfig, KNullDesC, aSuite);
+	}
+
+CStepData* CStepData::NewL(const TDesC& aStep, const TDesC& aConfig, const TDesC& aParamSet, CSuiteDll* aSuite)
+	{
+	CStepData* self = new(ELeave) CStepData;
+	CleanupStack::PushL(self);
+	self->ConstructL(aStep, aConfig, aParamSet, aSuite);
+	CleanupStack::Pop();
+	return self;
+	}
+
+CStepData::CStepData()
+	{
+	}
+
+CStepData::~CStepData()
+	{
+	}
+
+void CStepData::ConstructL(const TDesC& aStep, const TDesC& aConfig, const TDesC& aParamSet, CSuiteDll* aSuite)
+	{
+	iStep = aStep;
+	iConfig = aConfig;
+	iParamSet = aParamSet;
+	iSuite = aSuite;
+	}
+
+const TDesC& CStepData::ParamSet() const
+	{
+	return iParamSet;
+	}
+
+