egl/egltest/src/egltestcommonstep.cpp
changeset 0 5d03bc08d59c
child 26 15986eb6c500
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/egl/egltest/src/egltestcommonstep.cpp	Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,809 @@
+// Copyright (c) 2007-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:
+//
+
+/**
+ @file
+ @test
+*/
+
+#include <test/tefunit.h> // for ASSERT macros
+#ifndef __INIPARSER_H__
+#include <cinidata.h>
+#endif // __INIPARSER_H__
+#include <apgtask.h>
+#include <e32math.h>
+#include <e32msgqueue.h> 
+
+#include "egltestcommonstep.h"
+#include "egltestcommonsession.h"
+#include "egltestcommonutils.h"
+#include "egltestcommonprocess.h"
+
+static const TUint KDefaultHeapSize = 0x100000;
+CEglTestStep::TThreadStatus::TThreadStatus()
+    {
+    for(TInt i=0; i<ESize; i++)
+        {
+        iStatus[i] = 0;
+        }
+    }
+
+EXPORT_C CEglTestStep::CEglTestStep() : 
+    iWaitForCompletionOnPostamble(EFalse),
+	iSourceFormat(KDefaultSourceFormat),
+	iSurfaceFormat(KDefaultSurfaceFormat)
+	{
+	}
+
+EXPORT_C CEglTestStep::~CEglTestStep()
+	{
+	for (TInt i=0; i<KMaxProcessNumber; i++)
+		{
+		iProcessStatus[i].iProcess.Close();
+		}
+	TInt countThread = iThreadStatus.Count();
+	for (TInt i=0; i<countThread; i++)
+		{
+		iThreadStatus[i].iThread.Close();
+		}
+	iThreadStatus.Close();
+
+    iSemaphore[0].Close();
+    iSemaphore[1].Close();
+  
+    CleanAll();
+    CloseWsSession();
+	}
+
+EXPORT_C TVerdict CEglTestStep::doTestStepPreambleL()
+	{
+	User::LeaveIfError(Logger().ShareAuto());
+	//When EGL Logging is enabled this causes a file server session to be allocated
+	//Which needs to be done before any handle checks otherwise the test will fail
+	ASSERT_EGL_TRUE(eglReleaseThread());	
+	__UHEAP_MARK;
+	HandleMark();
+	return TestStepResult();
+	}
+
+EXPORT_C TVerdict CEglTestStep::doTestStepPostambleL()
+	{
+	if(iWaitForCompletionOnPostamble && (iThreadStatus.Count() > 0))
+        {
+        INFO_PRINTF1(_L("Main thread waits for other threads to be terminated!!"));
+        Test_MultiThread_WaitL(ETrue, TThreadStatus::ELogin);
+        }	
+	
+	if (iDisplay != EGL_NO_DISPLAY)
+		{
+		// Output a warning because this should be done by the test
+		WARN_PRINTF1(_L("Terminating Display during doTestStepPostambleL"));
+		ASSERT_EGL_TRUE(eglTerminate(iDisplay));
+		iDisplay = EGL_NO_DISPLAY;
+		}
+
+	ASSERT_EGL_TRUE(eglReleaseThread());
+
+	HandleMarkEnd();
+ 	__UHEAP_MARKEND;
+	return TestStepResult();
+	}
+
+EXPORT_C void CEglTestStep::CleanAll()
+	{
+	delete iEglSess;
+	iEglSess = NULL;
+	
+	if (iDisplay != EGL_NO_DISPLAY)
+		{
+		ASSERT_EGL_TRUE(eglTerminate(iDisplay));
+		iDisplay = EGL_NO_DISPLAY;
+		}
+	ASSERT_EGL_TRUE(eglReleaseThread());
+	}
+
+/*****************************************************************************
+ ** Utility methods
+ *****************************************************************************/
+
+void CEglTestStep::HandleMark()
+	{
+	RThread().HandleCount(iProcHandleMark, iThreadHandleMark);
+	INFO_PRINTF3(_L("MARK: (%d) process-owned handle(s) / (%d) thread-owned handle(s)"), iProcHandleMark, iThreadHandleMark);
+	}
+
+void CEglTestStep::HandleMarkEnd()
+	{
+	RThread().HandleCount(iProcHandleMarkEnd, iThreadHandleMarkEnd);
+	INFO_PRINTF3(_L("MARK-END: (%d) process-owned handle(s) / (%d) thread-owned handle(s)"), iProcHandleMarkEnd, iThreadHandleMarkEnd);
+#ifdef __WINS__
+	WARN_PRINTF1(_L("Process-owned handle test is ignored on WINS build due to Pls() behaviour."));
+#endif
+	// When using Pls() on WINS build, it inteferes with handle count assert here due to Pls() behaviour which initialises PLS object
+	// on first call of Pls() rather than during DLL loading, which cause extra count into iProcHandleMark.
+	// ARMV5 build does not suffer this problem as proper WSD support is used.
+#ifndef __WINS__
+	ASSERT_EQUALS(iProcHandleMarkEnd, iProcHandleMark);
+#endif
+	ASSERT_EQUALS(iThreadHandleMarkEnd, iThreadHandleMark);
+	}
+
+/** Initialises the window server session and window group objects. */
+EXPORT_C void CEglTestStep::OpenWsSessionL(TInt aGroupId)
+	{
+	User::LeaveIfError(iWsSession.Connect());
+	iWindowGroup = RWindowGroup(iWsSession);
+	User::LeaveIfError(iWindowGroup.Construct(aGroupId));
+	}
+
+/** Uninitialises the window group object and the window server session. */
+EXPORT_C void CEglTestStep::CloseWsSession()
+	{
+	iWindowGroup.Close();
+	iWsSession.Close();
+	}
+
+/**
+Uses the Eikon Environment to construct a window and put it on top.
+@param aWindow A non-constructed window object
+@param aRect The intial position and size of the window
+@leave Standard system errors
+*/
+EXPORT_C void CEglTestStep::ConstructWindowL(RWindow& aWindow, const TRect& aRect)
+	{
+	INFO_PRINTF1(_L("CEglTestStep::CreateWindowL()"));
+
+	const TUint32 ENullWsHandle = 0xFFFFFFFF;	// Events delivered to this handle are thrown away
+	aWindow = RWindow(iWsSession);
+	CleanupClosePushL(aWindow);
+	User::LeaveIfError(aWindow.Construct(iWindowGroup, ENullWsHandle));
+	aWindow.SetExtent(aRect.iTl, aRect.Size());
+	aWindow.Activate();
+	CleanupStack::Pop(&aWindow);
+	}
+
+/**
+Prints both the Source pixel format and the target pixel format
+*/
+EXPORT_C void CEglTestStep::PrintUsedPixelConfiguration()
+	{
+	INFO_PRINTF1(_L("******UsedPixelConfiguration******"));
+	INFO_PRINTF1(_L("Source Pixel Format"));
+	PrintPixelFormat(iSourceFormat);	
+	
+	INFO_PRINTF1(_L("Target Format"));
+	PrintVGImageFormat(iSurfaceFormat);
+	INFO_PRINTF1(_L("**********************************"));
+	}
+
+EXPORT_C void CEglTestStep::PrintPixelFormat(TUidPixelFormat aFormat)
+	{
+	switch(aFormat)
+		{
+        case EUidPixelFormatA_8:
+            INFO_PRINTF1(_L("EUidPixelFormatA_8"));
+            break;
+		case EUidPixelFormatRGB_565:
+			INFO_PRINTF1(_L("EUidPixelFormatRGB_565"));
+			break;
+		case EUidPixelFormatXRGB_8888:
+			INFO_PRINTF1(_L("EUidPixelFormatXRGB_8888"));
+			break;
+		case EUidPixelFormatARGB_8888:
+			INFO_PRINTF1(_L("EUidPixelFormatARGB_8888"));
+			break;
+		case EUidPixelFormatARGB_8888_PRE:
+			INFO_PRINTF1(_L("EUidPixelFormatARGB_8888_PRE"));
+			break;
+		default:
+			ERR_PRINTF2(_L("Unsupported pixel format (%d)"), aFormat);
+			ASSERT_TRUE(EFalse);
+		}
+	}
+
+EXPORT_C void CEglTestStep::PrintVGImageFormat(VGImageFormat aAttr)
+	{
+	switch(aAttr)
+		{
+		case VG_sRGB_565:
+			INFO_PRINTF1(_L("VG_sRGB_565"));
+			break;
+		case VG_sXRGB_8888:
+			INFO_PRINTF1(_L("VG_sXRGB_8888"));
+			break;
+		case VG_sARGB_8888:
+			INFO_PRINTF1(_L("VG_sARGB_8888"));
+			break;
+		case VG_sARGB_8888_PRE:
+			INFO_PRINTF1(_L("VG_sARGB_8888_PRE"));
+			break;
+		default:
+			ERR_PRINTF2(_L("Unsupported VGImage format (%d)"), aAttr);
+			ASSERT_TRUE(EFalse);
+		}
+	}
+
+
+/*****************************************************************************
+ ** Multiprocess test utils
+ *****************************************************************************/
+
+/**
+Launches the specified number of processes, where each process will perform the actions specified in 
+the doProcessFunctionL of the calling test. As no images TSgDrawableId has been passed, an 
+an array of one (NULL) TSgDrawableId will be created.
+@param aProcessCount Number of processes
+@param aTestName Name of the calling test case (so that it can call it's doProcessFunctionL method 
+@leave Standard system errors
+*/  
+EXPORT_C void CEglTestStep::Test_MultiProcessL(const TDesC& aTestDllName, TInt aProcessCount, const TDesC& aTestStepName)
+	{
+	TSgDrawableId sgId;
+	Mem::FillZ(&sgId, sizeof(TSgDrawableId));
+	Test_MultiProcessL(aTestDllName, aProcessCount, aTestStepName, sgId);
+	}
+
+/**
+Launches the specified number of processes, where each process will perform the actions specified in 
+the doProcessFunctionL of the calling test.
+@param aProcessCount Number of processes
+@param aTestName Name of the calling test case (so that it can call it's doProcessFunctionL method 
+@param aSgId Images TSgDrawableId which will be used to create an array of one TSgDrawableId
+@leave Standard system errors
+*/  
+EXPORT_C void CEglTestStep::Test_MultiProcessL(const TDesC& aTestDllName, TInt aProcessCount, const TDesC& aTestStepName, const TSgDrawableId& aSgId)
+	{
+	// we assume we pass the same Id to all the processes (array of one)
+	RArray<TSgDrawableId> sgIdList;
+	ASSERT_EQUALS(sgIdList.Insert(aSgId,0), KErrNone);
+	Test_MultiProcessL(aTestDllName, aProcessCount, aTestStepName, sgIdList);
+	sgIdList.Close();
+	}
+
+/**
+Launches the specified number of processes, where each process will perform the actions specified in 
+the doProcessFunctionL of the calling test. The association of images and processes is done via the
+predefined ImageIndexFromProcessId() method.
+@param aProcessCount Number of processes
+@param aTestName Name of the calling test case (so that it can call it's doProcessFunctionL method 
+@param aSgIdList Array containing the list of images' TSgDrawableId 
+@leave Standard system errors
+*/  
+EXPORT_C void CEglTestStep::Test_MultiProcessL(const TDesC& aTestDllName, TInt aProcessCount, const TDesC& aTestStepName, const RArray<TSgDrawableId>& aSgIdList)
+	{
+	TInt imageCount = aSgIdList.Count();
+	if(aProcessCount <= 0 || imageCount <=0 || aProcessCount > KMaxProcessNumber || imageCount > aProcessCount)
+		{
+		ERR_PRINTF1(_L("Invalid process request!"));
+		User::Leave(KErrArgument);
+		}
+
+    // create MsgQueue (only used in some test to pass data between 2 processes)
+	RMsgQueue<TSgDrawableId> messageQueueSgId;
+	TInt ret = messageQueueSgId.CreateGlobal(KNullDesC, 1, EOwnerProcess);
+	ASSERT_EQUALS(ret, KErrNone);
+
+	RMsgQueue<TProcessId> messageQueueProcId;
+	ret = messageQueueProcId.CreateGlobal(KNullDesC, 1, EOwnerProcess);
+	ASSERT_EQUALS(ret, KErrNone);
+
+    // Create semphores that can be shared (only used in some test to synch between 2 process)
+    ret = iSemaphore[0].CreateGlobal(KNullDesC(), 0, EOwnerProcess);
+    ASSERT_EQUALS(ret, KErrNone);
+    ret = iSemaphore[1].CreateGlobal(KNullDesC(), 0, EOwnerProcess);
+    ASSERT_EQUALS(ret, KErrNone);
+
+	for (TInt i=0; i<aProcessCount; i++)
+		{
+		TProcessInfo info;
+		info.iIdx=i;
+		info.iSgId=	aSgIdList[ImageIndexFromProcessId(i, imageCount)];
+
+		ret = iProcessStatus[i].iProcess.Create(KEglTestServerWrapperProcess, KNullDesC);
+		User::LeaveIfError(ret);
+      
+		// Specify the test for the process
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotTestDllName, aTestDllName);
+		User::LeaveIfError(ret);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotTestStepName, aTestStepName);
+		User::LeaveIfError(ret);	
+        
+		// Specify the non-handle params passed to the process
+		TPckg<TProcessInfo> pckgInfo(info);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotParams, pckgInfo);
+		User::LeaveIfError(ret);
+
+		// Pass in the semaphores
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotSemaphore0, iSemaphore[0]);
+		User::LeaveIfError(ret);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotSemaphore1, iSemaphore[1]);
+		User::LeaveIfError(ret);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotMsgQueueSgId, messageQueueSgId);
+		User::LeaveIfError(ret);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotMsgQueueProcId, messageQueueProcId);
+		User::LeaveIfError(ret);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotSourceFormat, static_cast<TInt>(iSourceFormat));
+		User::LeaveIfError(ret);
+		ret = iProcessStatus[i].iProcess.SetParameter(EProcSlotSurfaceFormat, static_cast<TInt>(iSurfaceFormat));
+		User::LeaveIfError(ret);
+		               
+		iProcessStatus[i].iProcess.Logon(iProcessStatus[i].iStatus); 
+		iProcessStatus[i].iProcess.Resume();
+		}
+
+	// wait for all processes to complete (not worried about the order)
+	// This is needed, as the only way to determine whether the process step has failed is to check
+	//   the return value (using TEST(EFalse) has no effect on the spawned process)
+	for (TInt i=0; i<aProcessCount; i++)
+		{
+		User::WaitForRequest(iProcessStatus[i].iStatus);
+		CheckProcessStatusL(i, iProcessStatus[i].iStatus, iProcessStatus[i].iProcess);
+		RDebug::Print(_L(">>>>>(%d)>> status :%d"), i, iProcessStatus[i].iStatus.Int());
+		iProcessStatus[i].iProcess.Close();
+		}
+
+	// close MsgQueue and semaphores (as used in some test with 2 spawned processes)
+	messageQueueSgId.Close();
+	messageQueueProcId.Close();
+	iSemaphore[1].Close();
+	iSemaphore[0].Close();
+	}
+
+/**
+Check the status of a process running as part of the current teststep. 
+@param aIndex Index of the process
+@param aStatus The request status of the process in question. 
+@param aProcess The process object itself.  
+@leave Standard system errors
+*/  
+void CEglTestStep::CheckProcessStatusL(TInt aIndex, const TRequestStatus& aStatus, const RProcess& aProcess)
+	{
+	TInt status = aStatus.Int();
+	if (status == KErrNone)
+		{
+		return;
+		}
+	if (status == KRequestPending)
+		{
+		// If the process is still running, that's an error, as we waited for the status  
+		ERR_PRINTF2(_L("Error in process %d - status should not be KRequestPending"), aIndex);
+		User::Leave(KErrTEFUnitFail);
+		}
+	// Something went wrong
+	switch (aProcess.ExitType())
+		{
+		case EExitPanic:		// The thread or process has been panicked.
+			{
+			TPtrC ptrExitCategory = aProcess.ExitCategory();
+			ERR_PRINTF4(_L("Panic in process %d - category:[%S] reason: %d"), aIndex, &ptrExitCategory, aProcess.ExitReason());
+			// Propagate the panic
+			User::Panic(aProcess.ExitCategory(), aProcess.ExitReason());
+			}
+			// follow through
+		case EExitKill: 		// The thread or process has ended as a result of a kill, i.e. Kill() has been called on the RThread or RProcess handle. Or a thread was ended as a result of calling User::Exit(). 
+		case EExitTerminate: 	// The thread or process has ended as a result of a terminate, i.e. Terminate() has been called on the RThread or RProcess handle. 
+		case EExitPending:		// The thread or process is alive. 
+		default:
+			// Propagate the error
+			ERR_PRINTF3(_L("Error in process %d - code %d"), aIndex, aStatus.Int());
+			User::Leave(aStatus.Int());
+		}
+	ASSERT(0);
+	}
+
+
+/*****************************************************************************
+ ** Multithread test utils
+ *****************************************************************************/
+
+/**
+Launches the specified number of threads, where each thread will perform the actions specified in 
+the doThreadFunctionL of the calling test. 
+@param aThreadCount Number of threads
+@param aWaitForCompletion To wait until the launched thread has completed 
+@leave Standard system errors
+*/  
+EXPORT_C void CEglTestStep::Test_MultiThreadL(TInt aThreadCount, TBool aWaitForCompletion)
+	{
+	if(aThreadCount <= 0 || aThreadCount > KMaxThreadNumber)
+		{
+		ERR_PRINTF1(_L("Invalid thread request!"));
+		User::Leave(KErrArgument);
+		}
+
+	iWaitForCompletionOnPostamble = !aWaitForCompletion;
+	
+	//we just care for these 2 semaphores
+	ASSERT_EQUALS(iSemaphore[0].CreateLocal(0, EOwnerProcess), KErrNone);
+	ASSERT_EQUALS(iSemaphore[1].CreateLocal(0, EOwnerProcess), KErrNone);
+	
+	_LIT(KThread, "CEglTestStep_Thread");
+	_LIT(KUnderScore, "_");
+ 	
+	TInt ret = KErrNone;
+	ASSERT_EQUALS(iThreadStatus.Count(),0);
+	// Reserve space to avoid reallocation of iThreadStatus.iStatus
+	iThreadStatus.ReserveL(aThreadCount);
+	for (TInt i=0; i<aThreadCount; i++)	
+		{
+		iThreadInfos[i].iSelf=this;
+		iThreadInfos[i].iIdx=i;
+ 
+		TTime tm;
+		TBuf<32> bufTime;
+		tm.UniversalTime();
+	    tm.FormatL(bufTime, _L("_%H%T%S%C_"));
+
+		// guaranteed unique thread name (useful if several threads are created with aWaitForCompletion = false)
+		TName threadName(KThread);
+		threadName.Append(KUnderScore);
+		threadName.AppendNum(i, EDecimal);
+		threadName.Append(KUnderScore);
+		threadName.Append(bufTime); 
+	    threadName.AppendNum(Math::Random(), EHex);
+
+	    iThreadStatus.AppendL(TThreadStatus());
+		ret = iThreadStatus[i].iThread.Create(threadName, ThreadFunction, KDefaultStackSize, KMinHeapSize, KDefaultHeapSize, &iThreadInfos[i], EOwnerProcess);
+		User::LeaveIfError(ret);
+
+		if(!aWaitForCompletion)
+			{
+			// We want to wait for the notification that the extra thread is about to be launched
+			// Improves timing issues within a hardware WDP environment
+			iThreadStatus[i].iThread.Rendezvous(iThreadStatus[i].iStatus[TThreadStatus::ERendezvous]);
+			}
+		iThreadStatus[i].iThread.Logon(iThreadStatus[i].iStatus[TThreadStatus::ELogin]);
+		iThreadStatus[i].iThread.Resume();
+		}
+    Test_MultiThread_WaitL(aWaitForCompletion, aWaitForCompletion ? TThreadStatus::ELogin : TThreadStatus::ERendezvous);
+   	}
+
+EXPORT_C void CEglTestStep::Test_MultiThread_WaitL(TBool aCloseThreads, TThreadStatus::TStatusId aStatusId)
+    {
+    // Close handles and wait for all threads to complete (not worried about the order). Note that some 
+    //   tests do not require to wait for completion. Nevertheless, care should be taken to ensure that the 
+    //   spawned thread is capable of modifying the main TEF process TestStepResult.
+       
+    TInt countThread = iThreadStatus.Count();
+    for (TInt i=0; i<countThread; i++)
+        {
+        User::WaitForRequest(iThreadStatus[i].iStatus[aStatusId]);
+        CheckThreadStatusL(i, iThreadStatus[i].iStatus[aStatusId], iThreadStatus[i].iThread);
+        INFO_PRINTF3(_L(">>>>>(%d)>> status :%d"), i, iThreadStatus[i].iStatus[aStatusId].Int());
+
+        if(aCloseThreads)
+            {
+            iThreadStatus[i].iThread.Close();
+            }
+        }
+    if(aCloseThreads)
+        {
+        iThreadStatus.Reset();
+
+        iSemaphore[0].Close();
+        iSemaphore[1].Close();
+        }
+    }
+
+/**
+Check the status of a thread running as part of the current teststep. 
+@param aIndex Index of the thread
+@param aStatus The request status of the thread in question. 
+@param aThread The thread object itself.  
+@leave Standard system errors
+*/ 
+void CEglTestStep::CheckThreadStatusL(TInt aIndex, const TRequestStatus& aStatus, const RThread& aThread)
+	{
+	TInt status = aStatus.Int();
+	if (status == KErrNone)
+		{
+		// All went well
+		return;
+		}
+	if (status == KRequestPending)
+		{
+		// If the thread is still running, that's an error, as we waited for the status  
+		ERR_PRINTF2(_L("Error in thread %d - status should not be KRequestPending"), aIndex);
+		User::Leave(KErrTEFUnitFail);
+		}
+	// Something went wrong
+	switch (aThread.ExitType())
+		{
+		case EExitPanic:		// The thread or process has been panicked.
+			{
+			TPtrC ptrExitCategory = aThread.ExitCategory();
+			ERR_PRINTF4(_L("Panic in thread %d - category:[%S] reason: %d"), aIndex, &ptrExitCategory, aThread.ExitReason());
+			User::Panic(aThread.ExitCategory(), aThread.ExitReason());
+			}
+			// follow through
+		case EExitKill: 		// The thread or process has ended as a result of a kill, i.e. Kill() has been called on the RThread or RProcess handle. Or a thread was ended as a result of calling User::Exit(). 
+		case EExitTerminate: 	// The thread or process has ended as a result of a terminate, i.e. Terminate() has been called on the RThread or RProcess handle. 
+		case EExitPending:		// The thread or process is alive. 
+		default:
+			// Propagate the error
+			ERR_PRINTF3(_L("Error in thread %d - code %d"), aIndex, status);
+			User::Leave(aStatus.Int());
+		}
+	// We should not get here!
+	ASSERT(0);
+	}
+
+TInt CEglTestStep::ThreadFunction(TAny* aInfo)
+// static
+	{
+	__UHEAP_MARK;
+	CTrapCleanup* cleanup = CTrapCleanup::New();
+	if(cleanup == NULL)
+		{
+		return KErrNoMemory;
+		}
+
+	CEglTestStep::TThreadInfo* info = reinterpret_cast<CEglTestStep::TThreadInfo*>(aInfo);
+	TRAPD(err, info->iSelf->ThreadFunctionL(*info));
+
+	delete cleanup;
+	__UHEAP_MARKEND;
+	return err;
+	}
+
+void CEglTestStep::ThreadFunctionL(TThreadInfo& aInfo)
+	{
+	// Mark the handle count for this thread
+	TInt processHandleMark=0;
+	TInt threadHandleMark=0;
+	RThread().HandleCount(processHandleMark, threadHandleMark);
+	INFO_PRINTF4(_L("MARK: THREAD %d: (%d) process-owned handle(s) / (%d) thread-owned handle(s)"), aInfo.iIdx, processHandleMark, threadHandleMark);	
+	
+	// Notify the main thread that we are about to launch the extra thread
+	RThread::Rendezvous(KErrNone);
+	
+	// Run the real thread funciton
+	aInfo.iSelf->doThreadFunctionL(aInfo.iIdx);
+
+	// Release EGL thread state
+	INFO_PRINTF2(_L("thread %d: Calling eglReleaseThread()"), aInfo.iIdx);
+	ASSERT_EGL_TRUE(eglReleaseThread());
+	
+	// Check the handle count for this thread has not changed
+	TInt processHandleMarkEnd=0;
+	TInt threadHandleMarkEnd=0;
+	RThread().HandleCount(processHandleMarkEnd, threadHandleMarkEnd);
+	INFO_PRINTF4(_L("MARK-END: THREAD %d: (%d) process-owned handle(s) / (%d) thread-owned handle(s)"), aInfo.iIdx, processHandleMarkEnd, threadHandleMarkEnd);
+	
+	//Not testing equality of process-owned handles as these should only be tested from the main thread.
+	//Process handlecount is dependent on all threads, therefore process handle imbalances could be the responsibility of other threads.
+	ASSERT_EQUALS(threadHandleMark, threadHandleMarkEnd);
+	}
+
+/**
+Tests should override this method for multithreaded testing
+*/
+EXPORT_C void CEglTestStep::doThreadFunctionL(TInt aIdx)
+	{
+	// Not supported for this test step
+	ERR_PRINTF2(_L("thread %d: Calling CEglTestStep::doThreadFunctionL() - should be overriden"), aIdx);
+	User::Leave(KErrNotSupported);
+	}
+
+EXPORT_C void CEglTestStep::doThreadFunctionL(TInt aIdx,const TSgDrawableId& aSgId)
+	{
+	// Not supported for this test step
+	ERR_PRINTF3(_L("thread %d: Calling CEglTestStep::doThreadFunctionL() - should be overriden, TSgDrawableId %lu."), aIdx, aSgId.iId);
+	User::Leave(KErrNotSupported);
+	}
+
+/**
+Tests should override this method for multiprocess testing
+*/
+EXPORT_C void CEglTestStep::doProcessFunctionL(TInt aIdx)
+	{
+	// Not supported for this test step
+	ERR_PRINTF2(_L("Process %d: Calling CEglTestStep::doProcessFunctionL() - should be overriden"), aIdx);
+	User::Leave(KErrNotSupported);
+	}
+
+EXPORT_C void CEglTestStep::doProcessFunctionL(TInt aIdx,const TSgDrawableId& aSgId)
+	{
+	// Not supported for this test step
+	ERR_PRINTF3(_L("Process %d: Calling CEglTestStep::doProcessFunctionL() - should be overriden, TSgDrawableId %lu."), aIdx, aSgId.iId);
+	User::Leave(KErrNotSupported);
+	}
+
+
+/**
+Rendezvous: Ensures that both threads get to this point before continuing
+@param aIdx The thread index value that was passed into
+			the override of CEglTestStep::doThreadFunctionL()
+*/
+EXPORT_C void CEglTestStep::Rendezvous(TInt aIdx)
+	{
+	if(aIdx >= 2)
+	    {
+	    // Currently Rendezvous is only supported between threads with index 0 and index 1
+	    INFO_PRINTF2(_L("CEglTestStep::Rendezvous() - aIdx (%d) is too big!!"), aIdx);
+	    ASSERT(0);
+	    }
+	INFO_PRINTF2(_L("thread %d: ...At Rendezvous..."), aIdx);
+	iSemaphore[aIdx].Signal();
+	iSemaphore[1-aIdx].Wait();
+	}
+
+
+/*****************************************************************************
+ ** Egl Helpers
+ *****************************************************************************/
+ 
+/**
+Temporarily initializes the EGL thread and display in order to check for the 
+supplied extension string.
+The display is then released and terminated.
+Use this method to pre-check for the existence of an extension string prior 
+to starting a test.
+There are 2 ways to ask for an extension, via the ID (the default way to do it) 
+or passing a string containing the full name of the extension (used in some tests only)
+@param aExtensions The extension ID to look for
+@param aExtensionName The extension name to look for
+@return Whether the extension string can be found
+*/
+EXPORT_C TBool CEglTestStep::CheckForExtensionL(TInt aExtensions, const TDesC& aExtensionName)
+	{
+	ASSERT_TRUE(iDisplay == EGL_NO_DISPLAY);
+	GetDisplayL();
+	CTestEglSession* eglSess = CTestEglSession::NewLC(Logger(), iDisplay, -1);
+	eglSess->InitializeL();
+	
+	TBool bFoundExtensions = eglSess->CheckNeededExtensionL(aExtensions, aExtensionName);
+	
+	// Cleanup EGL Completely
+	CleanupStack::PopAndDestroy(eglSess);
+	eglSess = NULL;
+	TerminateDisplayL();
+	ASSERT_EGL_TRUE(eglReleaseThread());
+
+	// return whether the extension string was found
+	return bFoundExtensions;
+	}
+
+/**
+Uses eglGetDisplay() to initialise iDisplay, and to check the result of the call
+*/
+EXPORT_C void CEglTestStep::GetDisplayL()
+	{
+	INFO_PRINTF1(_L("Calling eglGetDisplay..."));
+	
+	iDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+	ASSERT_EGL_TRUE(iDisplay != EGL_NO_DISPLAY);
+	}
+
+/**
+If the iDisplay has been initialised then this method uses eglTerminate() 
+to terminate iDisplay, and to check the result of the call.
+This method also resets the value of iDisplay to EGL_NO_DISPLAY to indicate
+that the display is no longer initialised.
+*/
+EXPORT_C void CEglTestStep::TerminateDisplayL()
+	{
+	if (iDisplay != EGL_NO_DISPLAY)
+		{
+		INFO_PRINTF1(_L("Calling eglTerminate..."));
+		ASSERT_EGL_TRUE(eglTerminate(iDisplay));
+		iDisplay = EGL_NO_DISPLAY;
+		}
+	}
+
+/**
+Cut and paste from CTestStep::SetLogger() - which is not exported
+As the name suggests, this is for use by the egl test process wrapper
+*/
+void CEglTestStep::SetLoggerForProcessWrapperL()
+	{
+	// Create a cinidata object for parsing the testexecute.ini
+	CTestExecuteIniData* parseTestExecuteIni = NULL;
+	TBuf<KMaxTestExecuteNameLength> resultFilePath;
+	TBuf<KMaxTestExecuteNameLength> xmlFilePath;
+	TInt logMode;
+	TInt logLevel;
+	
+	TRAPD(err,parseTestExecuteIni = CTestExecuteIniData::NewL());
+	if (err == KErrNone)
+		{
+		CleanupStack::PushL(parseTestExecuteIni);
+		parseTestExecuteIni->ExtractValuesFromIni();
+		parseTestExecuteIni->GetKeyValueFromIni(KTEFHtmlKey, resultFilePath);
+		parseTestExecuteIni->GetKeyValueFromIni(KTEFXmlKey, xmlFilePath);
+		parseTestExecuteIni->GetKeyValueFromIni(KTEFLogMode, logMode);
+		parseTestExecuteIni->GetKeyValueFromIni(KTEFLogSeverityKey, logLevel);
+		parseTestExecuteIni->GetKeyValueFromIni(KTEFEnableIniAccessLog, IniAccessLog());
+		}
+	else
+		{
+		resultFilePath.Copy(KTestExecuteLogPath);
+		xmlFilePath.Copy(KTestExecuteLogPath);
+		logMode = TLoggerOptions(ELogHTMLOnly);
+		logLevel = RFileFlogger::TLogSeverity(ESevrAll);
+		IniAccessLog() = ETrue;
+		}
+	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)
+		{
+		// For the old flogger we have to create an individual file
+		logFile.Copy(TestStepName());
+		_LIT(KTxtExtension,".txt");
+		logFile.Append(KTxtExtension);
+		logMode = TLoggerOptions(0);
+		Logger().SetLoggerOptions(logMode);
+		}
+	else
+		{
+		CleanupClosePushL(file);
+		TBuf8<KMaxTestExecuteNameLength> logFile8;
+		TInt fileSize;
+		User::LeaveIfError(file.Size(fileSize));
+		User::LeaveIfError(file.Read(logFile8,fileSize));
+		logFile.Copy(logFile8);
+		xmlLogFile.Append(logFile);
+		_LIT(KXmlExtension,".xml");
+		xmlLogFile.Append(KXmlExtension);
+		_LIT(KHtmExtension,".htm");
+		logFile.Append(KHtmExtension);
+		CleanupStack::Pop(&file);
+		file.Close();
+		}
+	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));
+		}
+	if (parseTestExecuteIni != NULL)
+		{
+		CleanupStack::PopAndDestroy(parseTestExecuteIni);
+		}
+	}
+
+EXPORT_C void CEglTestStep::PartialInitialiseL(const TDesC& aStepName)
+	{
+	SetTestStepName(aStepName);	
+	SetLoggerForProcessWrapperL();
+	// Assume pass
+	SetTestStepResult(EPass);
+	}
+
+EXPORT_C void CEglTestStep::CreateEglSessionL(TInt aIdx)
+	{
+	delete iEglSess; //just in case it was called twice
+	iEglSess = CTestEglSession::NewL(Logger(), iDisplay, aIdx);
+	}
+