userlibandfileserver/fileserver/runtests/runtests.cpp
changeset 9 96e5fb8b040d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/runtests/runtests.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,790 @@
+// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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:
+// f32\runtests\runtests.cpp
+// 
+//
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include <e32def_private.h>
+#include <e32svr.h>
+#include <e32ldr.h>
+#include <hal.h>
+#include "f32file.h"
+#include "f32dbg.h"
+#include "runtests.h"
+#include <u32hal.h>
+
+#define __PANIC(r) Panic(__LINE__,r)
+
+//#define _RUN_FOREVER_
+//#define _PANIC_ON_FAILURE_
+
+const TInt KDefaultTimeout=1200;		// 20 minutes
+const TInt KBackgroundTimeout=3000000;	// 3 seconds
+const TInt KDestroyTimeout=1000000;		// 1 second
+
+_LIT(KLitBackslash,"\\");
+_LIT(KDebugMessage, "Testing finished, and the system will be panicked");
+#ifdef __EPOC32__
+_LIT(KLitDefaultTestPath,"Z:\\TEST");
+#else
+_LIT(KLitDefaultTestPath,"");
+#endif
+
+#ifdef _DEBUG
+_LIT(KBuildType, "UDEB");
+#else
+_LIT(KBuildType, "UREL");
+#endif
+
+typedef RArray<TUint> RProcIdList;
+
+GLDEF_D TPath TheTestPath = KLitDefaultTestPath();
+GLDEF_D RFs TheFs;
+GLDEF_D RLoader TheLoaderSession;
+GLDEF_D TDesC8* TheTestList;
+GLDEF_D RTimer TheTimer;
+GLDEF_D TBuf<256> TheProcessCommand=KNullDesC();
+GLDEF_D TInt TheTimeOut = KDefaultTimeout;
+GLDEF_D TInt TheCurrentProcessList;
+GLDEF_D RProcIdList ProcLists[2];
+GLDEF_D RTimer IdleWaitTimer;
+GLDEF_D RTimer DestructionTimer;
+GLDEF_D RProperty CurrTest;
+TBool ShowTimings = 0;
+TInt TickPeriod = 15625;
+TBool CleanUpProcesses = EFalse;
+
+#ifdef _ENABLE_BTRACE_ANALYSIS_
+
+// BTrace analysis forward declarations
+const TInt KDefaultBTraceLevel = 0;
+TBool BtraceAnalysis = EFalse;
+TInt BTraceAnalysisLevel;
+TInt BTraceAnalyseSetup();
+void BTraceAnalyseEnd();
+void BTraceAnalyse(TInt aAnalysisLevel);
+
+#endif //_ENABLE_BTRACE_ANALYSIS_
+
+_LIT(KLitPanicCategory,"RUNTESTS-");
+_LIT(KLitLogPreamble,"RUNTESTS: ");
+
+void LogMsg(TRefByValue<const TDesC> aFmt,...);
+
+void DisableSimulatedFailure()
+	{
+	// Turn off simulated failure mechanisms for all base servers
+	TheFs.SetAllocFailure(KAllocFailureOff);	// F32 heap failure
+	TheFs.SetErrorCondition(KErrNone, 0);		// F32 other failure
+	TheLoaderSession.DebugFunction(ELoaderDebug_SetHeapFail, 0, 0, 0);	// Loader heap failure
+	TheLoaderSession.DebugFunction(ELoaderDebug_SetRFsFail, KErrNone, 0, 0);	// Loader RFs failure
+
+	// make sure kernel heap debug is off
+	__KHEAP_TOTAL_RESET;
+	}
+
+GLDEF_C void Panic(TInt aLine, TInt aReason)
+	{
+	TBuf<16> cat=KLitPanicCategory();
+	cat.AppendNum(aLine);
+	User::Panic(cat,aReason);
+	}
+
+TInt CloseAndWait(RHandleBase aH, TRequestStatus *aN = NULL)
+	{
+	TRequestStatus tempS;
+	if(!aN)
+		{
+		// Create a destruction notifier if none was supplied.
+		aH.NotifyDestruction(tempS);
+		aN = &tempS;
+		}
+	if (*aN!=KRequestPending)
+		{
+		User::WaitForRequest(*aN);
+		aH.Close();
+		return KErrNoMemory;
+		}
+	TRequestStatus t;
+	DestructionTimer.After(t, KDestroyTimeout);
+	aH.Close();
+	User::WaitForRequest(*aN, t);
+	if (*aN != KRequestPending)
+		{
+		DestructionTimer.Cancel();
+		User::WaitForRequest(t);
+		return KErrNone;
+		}
+	User::CancelMiscNotifier(*aN);
+	User::WaitForRequest(*aN);
+	return KErrTimedOut;
+	}
+
+void CloseWaitAndWarn(RHandleBase aH, TRequestStatus *aN = NULL)
+	{
+	TFullName fn(aH.FullName());
+	TInt r = CloseAndWait(aH, aN);
+	if (r == KErrNoMemory)
+		LogMsg(_L("WARNING OOM checking destruction of %S"), &fn);
+	else if (r == KErrTimedOut)
+		LogMsg(_L("ERROR Destruction of %S timed out"), &fn);
+	}
+
+TInt InitIdleWait()
+	{
+	TInt r = IdleWaitTimer.CreateLocal();
+	if (r!=KErrNone)
+		return r;
+	return KErrNone;
+	}
+
+void WaitForIdle()
+	{
+	TRequestStatus idle_req;
+	TRequestStatus timer_req;
+	IdleWaitTimer.After(timer_req, KBackgroundTimeout);
+	User::NotifyOnIdle(idle_req);
+	User::WaitForRequest(idle_req, timer_req);
+	if (idle_req != KRequestPending)
+		{
+		IdleWaitTimer.Cancel();
+		User::WaitForRequest(timer_req);
+		}
+	else
+		{
+		User::CancelMiscNotifier(idle_req);
+		User::WaitForRequest(idle_req);
+		LogMsg(_L("WARNING Excessive Background Activity Detected"));
+		}
+	}
+
+TBool IntentionallyPersistent(RProcess aProcess)
+	{
+	TInt v;
+	TInt r = RProperty::Get(aProcess.SecureId(), KRuntestsIntentionalPersistenceKey, v);
+	if (r==KErrNone && TUint(v)==KRuntestsIntentionalPersistenceValue)
+		return ETrue;
+	return EFalse;
+	}
+
+TInt GetProcessListThread(TAny* a)
+	{
+	RProcIdList& pl = *(RProcIdList*)a;
+	TFindProcess fp(_L("*"));
+	TFullName fn;
+	TInt r = KErrNone;
+	while (r==KErrNone && fp.Next(fn)==KErrNone)
+		{
+		RProcess p;
+		r = p.Open(fp, EOwnerThread);
+		if (r==KErrNone)
+			{
+			TUint id = (TUint)p.Id();
+			r = pl.Append(id);
+			p.Close();
+			}
+		}
+	return r;
+	}
+
+TInt GetProcessList(RProcIdList& aList)
+	{
+	aList.Reset();
+	RThread t;
+	TRequestStatus s;
+	TInt r = t.Create(KNullDesC, &GetProcessListThread, 0x1000, NULL, &aList);
+	if (r==KErrNone)
+		{
+		t.Logon(s);
+		t.SetPriority(EPriorityAbsoluteHigh);
+		if (s==KRequestPending)
+			t.Resume();
+		User::WaitForRequest(s);
+		r=s.Int();
+		if (t.ExitType()==EExitPending)
+			{
+			t.Kill(0);
+			WaitForIdle();
+			}
+		else if (t.ExitType()!=EExitKill)
+			{
+			r = -99;
+			}
+		CloseWaitAndWarn(t);
+		}
+	aList.Sort();
+	return r;
+	}
+
+TBool ParseNumber(TLex& aLex, TUint& aNumber, TBool isTime)
+	{
+	TPtrC numberDes = aLex.NextToken();
+	TInt len = numberDes.Length();
+	if (len == 0)
+		{
+		return EFalse;
+		}
+
+	aNumber = 0;
+	TInt magnitude = 1;
+	TChar c = numberDes[len-1];
+	if (isTime)
+		{
+		switch (c)
+			{
+			case 'h':
+			case 'H':
+				len -= 1;
+				magnitude = 3600;
+				break;
+
+			case 'm':
+			case 'M':
+				len -= 1;
+				/*FALLTHRU*/
+			default:
+				magnitude = 60;
+				break;
+
+			case 's':
+			case 'S':
+				len -= 1;
+				magnitude = 1;
+				break;
+			}
+		}
+
+	for (TInt i = len-1; i >= 0; --i)
+		{
+		c = numberDes[i];
+		if (c < '0' || c > '9')
+			__PANIC(KErrArgument);
+		aNumber += ((TInt)c-'0')*magnitude;
+		magnitude *= 10;
+		}
+
+	return ETrue;
+	}
+
+void GetTimeOut(TLex& aLex)
+//
+//
+//
+	{
+	TheTimeOut = KDefaultTimeout;
+	TUint timeOut = 0;
+	if (ParseNumber(aLex, timeOut, ETrue))
+		{
+		TheTimeOut = timeOut;
+		}
+	}
+
+#ifdef _ENABLE_BTRACE_ANALYSIS_
+
+void GetAnalysisLevel(TLex& aLex)
+	{
+	BTraceAnalysisLevel = KDefaultBTraceLevel;
+	TUint level;
+	if (ParseNumber(aLex, level, EFalse))
+		{
+		BTraceAnalysisLevel = level;
+		}
+	}
+#endif //_ENABLE_BTRACE_ANALYSIS_
+
+void LogMsg(TRefByValue<const TDesC> aFmt,...)
+	{
+	VA_LIST list;
+	VA_START(list,aFmt);
+	TBuf<0x100> buf=KLitLogPreamble();
+	buf.AppendFormatList(aFmt,list);
+	RDebug::Print(_L("%S"),&buf);
+	}
+
+_LIT(KLitError, "Error ");
+TBool LogProcess(TUint aId, TBool aInit)
+	{
+	TFullName pn;
+	TFileName fn;
+	RProcess p;
+	TBool killed = EFalse;
+	TInt r = p.Open(TProcessId(aId));
+	if (r==KErrNone)
+		{
+		if (IntentionallyPersistent(p))
+			{
+			p.Close();
+			return killed;
+			}
+		pn = p.FullName();
+		fn = p.FileName();
+		if (!aInit && CleanUpProcesses && p.ExitType()==EExitPending)
+			{// p is a left over process so terminate it.
+			killed = ETrue;
+			TRequestStatus status;
+			p.Logon(status);
+			p.Kill(KErrNone);	// Kill with KErrNone to suppress extra debug output from kernel.
+			User::WaitForRequest(status);
+			CloseAndWait(p);
+			}
+		else
+			{
+			p.Close();
+			}
+		}
+	else
+		{
+		pn = KLitError;
+		pn.AppendNum(r);
+		}
+	if (aInit)
+		LogMsg(_L("Running process id=%d: %S (%S)"),aId,&pn,&fn);
+	else
+		{
+		if(killed)
+			LogMsg(_L("ERROR Leftover process was killed id=%d: %S (%S)"),aId,&pn,&fn);
+		else
+			LogMsg(_L("ERROR Leftover process id=%d: %S (%S)"),aId,&pn,&fn);
+		}
+	return killed;
+	}
+
+void ListProcesses()
+	{
+	RProcIdList& cur_list = ProcLists[TheCurrentProcessList];
+	TInt cc = cur_list.Count();
+	TInt ci;
+	for (ci=0; ci<cc; ++ci)
+		{
+		LogProcess(cur_list[ci], ETrue);
+		}
+	}
+
+void CheckProcesses()
+	{
+	RProcIdList& cur_list = ProcLists[TheCurrentProcessList];
+	RProcIdList& new_list = ProcLists[1-TheCurrentProcessList];
+	TInt r = GetProcessList(new_list);
+	if (r!=KErrNone)
+		{
+		LogMsg(_L("WARNING Problem getting process list, error %d"),r);
+		return;
+		}
+
+	TInt cc = cur_list.Count();
+	TInt nc = new_list.Count();
+	TInt ci = 0;
+	TInt ni = 0;
+	while (ci<cc || ni<nc)
+		{
+		TUint id1=0;
+		TUint id2=0;
+		if (ci<cc)
+			id1 = cur_list[ci];
+		if (ni<nc)
+			id2 = new_list[ni];
+		if (ci==cc)
+			{
+			// extra process has appeared so kill it and output an error message.
+			if (LogProcess(id2, EFalse))
+				{// Remove from list as we don't want it to be considered as vanished when the next test completes.
+				new_list.Remove(ni);
+				nc--;
+				}
+			else
+				{// Extra process was left running so just move onto the next one.
+				ni++;
+				}
+			}
+		else if (ni<nc && id1==id2)
+			{
+			// process remains
+			++ci, ++ni;
+			}
+		else
+			{
+			// process has disappeared
+			LogMsg(_L("WARNING Vanished process, id=%d"),id1);
+			++ci;
+			}
+		}
+
+	// current list = new list
+	TheCurrentProcessList = 1 - TheCurrentProcessList;
+
+	// throw away the old list
+	cur_list.Reset();
+	}
+
+_LIT8 (KLitRemark,"REM");
+_LIT8 (KLitAtRemark,"@REM");
+
+void ProcessLine(const TDesC8& aLine)
+	{
+	TLex8 lex(aLine);
+	TPtrC8 testname=lex.NextToken();
+	if (testname.Length()>=2 && testname[0]=='/' && testname[1]=='/')
+		return;
+	// ignore this line if it begins with rem or @rem 
+	if (testname.CompareF(KLitRemark) == 0 || testname.CompareF(KLitAtRemark) == 0)
+		return;
+	TFileName testnameU;
+	testnameU.Copy(testname);
+	TFileName fullpathname;
+	if (testnameU.Locate(TChar('\\'))==KErrNotFound)
+		fullpathname=TheTestPath;
+	fullpathname+=testnameU;
+	if (testname.Locate(TChar('.'))==KErrNotFound)
+		fullpathname+=_L(".EXE");
+	TInt r;
+
+	RFile file;
+	r=file.Open(TheFs,fullpathname,EFileRead);
+	if (r!=KErrNone)
+		{
+		// Remove path to let loader locate exe
+		fullpathname = fullpathname.Mid(fullpathname.LocateReverse('\\')+1);
+		}
+	else
+		file.Close();
+
+	RProcess p;
+	if(TheProcessCommand==KNullDesC)
+		{
+		TheProcessCommand.Copy(lex.Remainder());
+		r=p.Create(fullpathname, TheProcessCommand);
+		TheProcessCommand=KNullDesC();
+		}
+	else
+		r=p.Create(fullpathname, TheProcessCommand);
+	if (r!=KErrNone)
+		{
+		LogMsg(_L("Test %S ERROR Could not load file, error %d"),&fullpathname,r);
+		return;
+		}
+	else
+		{
+		LogMsg(_L("Started test %S"),&fullpathname);
+		}
+	TRequestStatus ds;
+	p.NotifyDestruction(ds);	// allocate the destruction notifier early so that it doesn't get flagged as a leak by kernel heap checking in e.g., efile (DEF133800)
+	CurrTest.Set(p.FileName());
+	User::After(100000);		// allow latency measurements to be output
+	p.SetJustInTime(EFalse);	// we don't want the automatic test run to be halted by the debugger
+	TRequestStatus ps;
+	p.Rendezvous(ps);
+	TInt time_remain = TheTimeOut;
+	TRequestStatus ts;
+	TUint start = User::TickCount();
+	p.Resume();
+	TBool persist = EFalse;
+	TBool timer_running = EFalse;
+	FOREVER
+		{
+		TInt nsec = Min(time_remain, 1800);
+		if (!timer_running)
+			TheTimer.After(ts, nsec*1000000);
+		timer_running = ETrue;
+		User::WaitForRequest(ps,ts);
+		if (ps!=KRequestPending)
+			{
+			if (p.ExitType()==EExitPending)
+				{
+				// rendezvous completed but process not terminated
+				if (!IntentionallyPersistent(p))
+					{
+					// not persistent - wait for process to terminate
+					p.Logon(ps);
+					continue;
+					}
+				persist = ETrue;
+				}
+			break;
+			}
+		timer_running = EFalse;
+		time_remain -= nsec;
+		if (time_remain==0)
+			{
+			LogMsg(_L("Going to kill test %S: it's taken %u seconds, which is too long"),&fullpathname,TheTimeOut);
+			p.Kill(0);
+			User::WaitForRequest(ps);
+			p.Logon(ps);
+			User::WaitForRequest(ps);
+
+			CloseWaitAndWarn(p, &ds);
+			RDebug::Print(_L("\n"));
+			LogMsg(_L("Test %S TIMEOUT"),&fullpathname);
+			return;
+			}
+		else
+			{
+			LogMsg(_L("Taken %u seconds so far"),TheTimeOut-time_remain);
+			}
+		}
+	TUint end = User::TickCount();
+	if(timer_running)
+		{
+		TheTimer.Cancel();
+		User::WaitForRequest(ts);
+		}
+
+#ifdef _ENABLE_BTRACE_ANALYSIS_
+	//
+	//
+	//
+	if (BtraceAnalysis)
+		{// Analyse BTrace buffer
+		BTraceAnalyse(BTraceAnalysisLevel);
+		}
+#endif //_ENABLE_BTRACE_ANALYSIS_
+
+	TBuf<32> exitCat=p.ExitCategory();
+	TExitType exitType=p.ExitType();
+	TInt exitReason=p.ExitReason();
+	if (persist || (exitType==EExitKill && exitReason==KErrNone))
+		{
+		TUint time = TUint((TUint64)(end-start)*(TUint64)TickPeriod/(TUint64)1000000);
+		if(ShowTimings)
+			{
+			LogMsg(_L("Test %S OK - Seconds Taken: %u"),&fullpathname, time);
+			}
+		else
+			{
+			LogMsg(_L("Test %S OK"),&fullpathname);
+			}
+		if (persist)
+			{
+			// We do not need this destruction notifier so cancel it.
+			User::CancelMiscNotifier(ds);
+			User::WaitForRequest(ds);
+			
+			p.Close();
+			}
+		else
+			{
+			CloseWaitAndWarn(p, &ds);
+			}
+		return;
+		}
+	LogMsg(_L("Test %S FAIL - Exit code %d,%d,%S"),&fullpathname,exitType,exitReason,&exitCat);
+	CloseWaitAndWarn(p, &ds);
+#if defined(_PANIC_ON_FAILURE_)
+	__PANIC(KErrGeneral);
+#endif
+	}
+
+void ProcessTestList()
+	{
+	TUint start = User::TickCount();
+
+	TLex8 llex(*TheTestList);
+	while(!llex.Eos())
+		{
+		llex.SkipSpace();
+		llex.Mark();
+		while(!llex.Eos() && llex.Peek()!='\n' && llex.Peek()!='\r')
+			llex.Inc();
+		TPtrC8 line=llex.MarkedToken();
+		if (line.Length()!=0)
+			ProcessLine(line);
+
+		// allow cleanup to complete before starting the next test
+		WaitForIdle();
+
+		// make sure simulated failure is off
+		DisableSimulatedFailure();
+
+		// check for leftover processes
+		CheckProcesses();
+		
+		// Reset the demand paging cache to its default size.
+		UserSvr::HalFunction(EHalGroupVM,EVMHalSetCacheSize, 0, 0);
+		}
+
+	TUint end = User::TickCount();
+	TUint time = TUint((TUint64)(end-start)*(TUint64)TickPeriod/(TUint64)1000000);
+	LogMsg(_L("Elapsed Seconds: %u"), time);
+	}
+
+void Help()
+	{
+	RDebug::Print(_L("Runtests test list [-x where tests are] [-d drive letter to test] [-t timeout] [-p] [-st] [-c]"));
+	RDebug::Print(_L("where -p sets runtests to be system permanent, -st enables timing information,"));
+	RDebug::Print(_L("-c enables left over processes to be cleaned up"));
+	}
+
+GLDEF_C TInt E32Main()
+	{
+	HAL::Get(HAL::ESystemTickPeriod, TickPeriod);
+	RThread().SetPriority(EPriorityAbsoluteHigh);
+	TBuf<0x100> cmd;
+	User::CommandLine(cmd);
+	TFileName thisfile=RProcess().FileName();
+	TLex lex(cmd);
+	TPtrC token=lex.NextToken();
+	if (token.MatchF(thisfile)==0)
+		{
+		token.Set(lex.NextToken());
+		}
+	if (token.Length()==0)
+		{//	__PANIC(0);
+		Help();
+		return 0;
+		}
+	TFileName listfilename=token;
+	while (!lex.Eos())
+		{
+		token.Set(lex.NextToken());
+		if (token.Length()==0)
+			break;	// ignore trailing whitespace
+		else if (token==_L("-x"))
+			{
+			token.Set(lex.NextToken());
+			TheTestPath = token;
+			}
+		else if (token==_L("-d"))
+			{
+			token.Set(lex.NextToken());
+			TheProcessCommand = token;
+			}
+		else if (token==_L("-t"))
+			{
+			GetTimeOut(lex);
+			}
+		else if (token==_L("-p"))
+			{
+			User::SetCritical(User::ESystemPermanent);
+			}
+		else if (token==_L("-st"))
+			ShowTimings = 1;
+		
+#ifdef _ENABLE_BTRACE_ANALYSIS_		
+		else if (token == _L("-a"))
+			{
+			BtraceAnalysis = ETrue;
+			GetAnalysisLevel(lex);
+			TInt r = BTraceAnalyseSetup();
+			if (r != KErrNone)
+				{
+				RDebug::Print(_L("ERROR - Couldn't open BTrace driver (Code %d)"), r);
+				return 0;
+				}
+			}
+#endif //_ENABLE_BTRACE_ANALYSIS_
+
+		else if (token == _L("-c"))
+			CleanUpProcesses = ETrue;
+		else
+			{
+			RDebug::Print(_L("Unknown option %S"), &token);
+			Help();
+			return 0;
+			}
+		}
+		
+	RDebug::Print(_L("TPTT= %S \n"), &TheProcessCommand);											
+	RDebug::Print(_L("TTL= %S \n"), &listfilename);
+	RDebug::Print(_L("TTP= %S \n"), &TheTestPath);
+	RDebug::Print(_L("TO= %d seconds\n"), TheTimeOut);
+
+	TInt l=TheTestPath.Length();
+	if (l > 0 && TheTestPath[l-1]!='\\')
+		TheTestPath+=KLitBackslash;
+	if (listfilename.Locate(TChar('\\'))==KErrNotFound)
+		listfilename.Insert(0,TheTestPath);
+	TInt r=TheFs.Connect();
+	if (r!=KErrNone)
+		__PANIC(r);
+	r = TheLoaderSession.Connect();
+	if (r!=KErrNone)
+		__PANIC(r);
+	DisableSimulatedFailure();
+	r=TheFs.SetSessionPath(_L("Z:\\test\\"));
+	if (r!=KErrNone)
+		__PANIC(r);
+	r=TheTimer.CreateLocal();
+	if (r!=KErrNone)
+		__PANIC(r);
+	RFile listfile;
+	r=listfile.Open(TheFs,listfilename,EFileRead|EFileShareAny);
+	if (r!=KErrNone)
+		__PANIC(r);
+	TInt listfilesize;
+	r=listfile.Size(listfilesize);
+	if (r!=KErrNone)
+		__PANIC(r);
+	HBufC8* pL=HBufC8::New(listfilesize);
+	if (!pL)
+		__PANIC(KErrNoMemory);
+	TPtr8 ptr=pL->Des();
+	TheTestList=pL;
+	r=listfile.Read(ptr);
+	if (r!=KErrNone)
+		__PANIC(r);
+	listfile.Close();
+	LogMsg(_L("Running test script %S"),&listfilename);
+	LogMsg(_L("Build %S"),&KBuildType);
+	LogMsg(_L("Path to test %S"),&TheProcessCommand);
+
+	r = RProperty::Define(	KRuntestsCurrentTestKey,
+							RProperty::EText,
+							TSecurityPolicy(TSecurityPolicy::EAlwaysPass),
+							TSecurityPolicy(RProcess().SecureId()),
+							512
+						);
+	if (r!=KErrNone && r!=KErrAlreadyExists)
+		__PANIC(r);
+	r = CurrTest.Attach(RProcess().SecureId(), KRuntestsCurrentTestKey);
+	if (r!=KErrNone)
+		__PANIC(r);
+	r = CurrTest.Set(KNullDesC);
+	if (r!=KErrNone)
+		__PANIC(r);
+
+	r = DestructionTimer.CreateLocal();
+	if (r!=KErrNone)
+		__PANIC(r);
+	TheCurrentProcessList = 0;
+	r = GetProcessList(ProcLists[0]);
+	if (r!=KErrNone)
+		__PANIC(r);
+	ListProcesses();
+	r = InitIdleWait();
+	if (r!=KErrNone)
+		__PANIC(r);
+#if defined(_RUN_FOREVER_)
+	FOREVER
+#endif
+	ProcessTestList();
+	r = CurrTest.Set(KNullDesC);
+	if (r!=KErrNone)
+		__PANIC(r);
+	CurrTest.Close();
+	User::After(1000000);	// allow latency measurements to be output before exiting
+	LogMsg(_L("Completed test script %S"),&listfilename);
+	TheLoaderSession.Close();
+	TheFs.Close();
+	TheTimer.Close();
+	IdleWaitTimer.Close();
+	DestructionTimer.Close();
+
+#ifdef _ENABLE_BTRACE_ANALYSIS_
+	BTraceAnalyseEnd();
+#endif //_ENABLE_BTRACE_ANALYSIS_
+	if(User::Critical()==User::ESystemPermanent)
+		RDebug::Print(KDebugMessage);
+	return KErrNone;
+	}