Refactor tcu::TestExecutor

tcu::TestExecutor used to provide all functionality required for
traversing hierarchy, executing tests, and even generating test case
lists. This change splits TestExecutor into three separate utilities:

 1) TestHierarchyIterator provides test hierarchy traversal.
 2) TestSessionExecutor provides test session execution.
 3) Utility functions for generating test case list files.

In addition TestCaseWrapper has been replaced by package-specific
TestCaseExecutor with TestSessionExecutor-managed lifetime.

Change-Id: Idb8090964a80cd64892231798710547e84e2989b
diff --git a/Android.mk b/Android.mk
index 2868a95..2115e14 100644
--- a/Android.mk
+++ b/Android.mk
@@ -38,6 +38,7 @@
 	framework/common/tcuCompressedTexture.cpp \
 	framework/common/tcuCPUWarmup.cpp \
 	framework/common/tcuDefs.cpp \
+	framework/common/tcuEither.cpp \
 	framework/common/tcuFactoryRegistry.cpp \
 	framework/common/tcuFloat.cpp \
 	framework/common/tcuFloatFormat.cpp \
@@ -47,29 +48,29 @@
 	framework/common/tcuImageIO.cpp \
 	framework/common/tcuInterval.cpp \
 	framework/common/tcuMatrix.cpp \
+	framework/common/tcuMaybe.cpp \
 	framework/common/tcuPlatform.cpp \
 	framework/common/tcuRandomValueIterator.cpp \
 	framework/common/tcuRenderTarget.cpp \
 	framework/common/tcuResource.cpp \
 	framework/common/tcuResultCollector.cpp \
 	framework/common/tcuRGBA.cpp \
-	framework/common/tcuEither.cpp \
+	framework/common/tcuSeedBuilder.cpp \
 	framework/common/tcuStringTemplate.cpp \
 	framework/common/tcuSurface.cpp \
 	framework/common/tcuTestCase.cpp \
-	framework/common/tcuTestCaseWrapper.cpp \
 	framework/common/tcuTestContext.cpp \
-	framework/common/tcuTestExecutor.cpp \
+	framework/common/tcuTestHierarchyIterator.cpp \
+	framework/common/tcuTestHierarchyUtil.cpp \
 	framework/common/tcuTestLog.cpp \
 	framework/common/tcuTestPackage.cpp \
+	framework/common/tcuTestSessionExecutor.cpp \
 	framework/common/tcuTexCompareVerifier.cpp \
 	framework/common/tcuTexLookupVerifier.cpp \
 	framework/common/tcuTexture.cpp \
 	framework/common/tcuTextureUtil.cpp \
 	framework/common/tcuTexVerifierUtil.cpp \
 	framework/common/tcuThreadUtil.cpp \
-	framework/common/tcuSeedBuilder.cpp \
-	framework/common/tcuMaybe.cpp \
 	framework/delibs/debase/deDefs.c \
 	framework/delibs/debase/deFloat16.c \
 	framework/delibs/debase/deInt32.c \
@@ -324,7 +325,6 @@
 	modules/gles31/tes31Context.cpp \
 	modules/gles31/tes31InfoTests.cpp \
 	modules/gles31/tes31TestCase.cpp \
-	modules/gles31/tes31TestCaseWrapper.cpp \
 	modules/gles31/tes31TestPackage.cpp \
 	modules/gles31/tes31TestPackageEntry.cpp \
 	modules/gles3/accuracy/es3aAccuracyTests.cpp \
@@ -463,7 +463,6 @@
 	modules/gles3/tes3Context.cpp \
 	modules/gles3/tes3InfoTests.cpp \
 	modules/gles3/tes3TestCase.cpp \
-	modules/gles3/tes3TestCaseWrapper.cpp \
 	modules/gles3/tes3TestPackage.cpp \
 	modules/gles3/tes3TestPackageEntry.cpp \
 	modules/glshared/glsAttributeLocationTests.cpp \
diff --git a/framework/common/CMakeLists.txt b/framework/common/CMakeLists.txt
index 20ad1dc..9529b56 100644
--- a/framework/common/CMakeLists.txt
+++ b/framework/common/CMakeLists.txt
@@ -46,12 +46,10 @@
 	tcuSurface.hpp
 	tcuTestCase.cpp
 	tcuTestCase.hpp
-	tcuTestCaseWrapper.cpp
-	tcuTestCaseWrapper.hpp
 	tcuTestContext.cpp
 	tcuTestContext.hpp
-	tcuTestExecutor.cpp
-	tcuTestExecutor.hpp
+	tcuTestSessionExecutor.cpp
+	tcuTestSessionExecutor.hpp
 	tcuTestLog.cpp
 	tcuTestLog.hpp
 	tcuTestPackage.cpp
@@ -85,6 +83,10 @@
 	tcuMaybe.cpp
 	tcuEither.hpp
 	tcuEither.cpp
+	tcuTestHierarchyIterator.cpp
+	tcuTestHierarchyIterator.hpp
+	tcuTestHierarchyUtil.cpp
+	tcuTestHierarchyUtil.hpp
 	)
 
 set(TCUTIL_LIBS
diff --git a/framework/common/tcuApp.cpp b/framework/common/tcuApp.cpp
index 0981044..a16e45e 100644
--- a/framework/common/tcuApp.cpp
+++ b/framework/common/tcuApp.cpp
@@ -24,7 +24,8 @@
 #include "tcuApp.hpp"
 #include "tcuPlatform.hpp"
 #include "tcuTestContext.hpp"
-#include "tcuTestExecutor.hpp"
+#include "tcuTestSessionExecutor.hpp"
+#include "tcuTestHierarchyUtil.hpp"
 #include "tcuCommandLine.hpp"
 #include "tcuTestLog.hpp"
 #include "qpInfo.h"
@@ -34,19 +35,6 @@
 namespace tcu
 {
 
-static void watchDogTimeoutFunc (qpWatchDog* watchDog, void* userPtr)
-{
-	DE_UNREF(watchDog);
-	static_cast<App*>(userPtr)->onWatchdogTimeout();
-}
-
-static void crashHandlerFunc (qpCrashHandler* crashHandler, void* userPtr)
-{
-	DE_UNREF(crashHandler);
-	static_cast<App*>(userPtr)->onCrash();
-}
-
-
 /*--------------------------------------------------------------------*//*!
  * \brief Construct test application
  *
@@ -61,6 +49,7 @@
 	, m_crashHandler	(DE_NULL)
 	, m_crashed			(false)
 	, m_testCtx			(DE_NULL)
+	, m_testRoot		(DE_NULL)
 	, m_testExecutor	(DE_NULL)
 {
 	print("dEQP Core %s (0x%08x) starting..\n", qpGetReleaseName(), qpGetReleaseId());
@@ -71,29 +60,48 @@
 
 	try
 	{
+		const RunMode	runMode	= cmdLine.getRunMode();
+
 		// Initialize watchdog
 		if (cmdLine.isWatchDogEnabled())
-			TCU_CHECK(m_watchDog = qpWatchDog_create(watchDogTimeoutFunc, this, 300, 30));
+			TCU_CHECK_INTERNAL(m_watchDog = qpWatchDog_create(onWatchdogTimeout, this, 300, 30));
 
 		// Initialize crash handler.
 		if (cmdLine.isCrashHandlingEnabled())
-			TCU_CHECK(m_crashHandler = qpCrashHandler_create(crashHandlerFunc, this));
+			TCU_CHECK_INTERNAL(m_crashHandler = qpCrashHandler_create(onCrash, this));
 
 		// Create test context
 		m_testCtx = new TestContext(m_platform, archive, log, cmdLine, m_watchDog);
 
-		// Create test executor
-		m_testExecutor = new TestExecutor(*m_testCtx, cmdLine);
+		// Create root from registry
+		m_testRoot = new TestPackageRoot(*m_testCtx, TestPackageRegistry::getSingleton());
+
+		// \note No executor is created if runmode is not EXECUTE
+		if (runMode == RUNMODE_EXECUTE)
+			m_testExecutor = new TestSessionExecutor(*m_testRoot, *m_testCtx);
+		else if (runMode == RUNMODE_DUMP_XML_CASELIST)
+			writeXmlCaselists(*m_testRoot, *m_testCtx, m_testCtx->getCommandLine());
+		else if (runMode == RUNMODE_DUMP_TEXT_CASELIST)
+			writeTxtCaselists(*m_testRoot, *m_testCtx, m_testCtx->getCommandLine());
+		else
+			DE_ASSERT(false);
 	}
 	catch (const std::exception& e)
 	{
+		cleanup();
 		die("Failed to initialize dEQP: %s", e.what());
 	}
 }
 
 App::~App (void)
 {
+	cleanup();
+}
+
+void App::cleanup (void)
+{
 	delete m_testExecutor;
+	delete m_testRoot;
 	delete m_testCtx;
 
 	if (m_crashHandler)
@@ -103,7 +111,6 @@
 		qpWatchDog_destroy(m_watchDog);
 }
 
-
 /*--------------------------------------------------------------------*//*!
  * \brief Step forward test execution
  * \return true if application should call iterate() again and false
@@ -111,8 +118,14 @@
  *//*--------------------------------------------------------------------*/
 bool App::iterate (void)
 {
+	if (!m_testExecutor)
+	{
+		DE_ASSERT(m_testCtx->getCommandLine().getRunMode() != RUNMODE_EXECUTE);
+		return false;
+	}
+
 	// Poll platform events
-	bool platformOk = m_platform.processEvents();
+	const bool platformOk = m_platform.processEvents();
 
 	// Iterate a step.
 	bool testExecOk = false;
@@ -138,7 +151,7 @@
 		const RunMode runMode = m_testCtx->getCommandLine().getRunMode();
 		if (runMode == RUNMODE_EXECUTE)
 		{
-			const TestRunResult& result = m_testExecutor->getResult();
+			const TestRunStatus& result = m_testExecutor->getStatus();
 
 			// Report statistics.
 			print("\nTest run totals:\n");
@@ -154,13 +167,16 @@
 	return platformOk && testExecOk;
 }
 
-/*--------------------------------------------------------------------*//*!
- * \brief Get test run result
- * \return Current test run result.
- *//*--------------------------------------------------------------------*/
-const TestRunResult& App::getResult (void) const
+void App::onWatchdogTimeout (qpWatchDog* watchDog, void* userPtr)
 {
-	return m_testExecutor->getResult();
+	DE_UNREF(watchDog);
+	static_cast<App*>(userPtr)->onWatchdogTimeout();
+}
+
+void App::onCrash (qpCrashHandler* crashHandler, void* userPtr)
+{
+	DE_UNREF(crashHandler);
+	static_cast<App*>(userPtr)->onCrash();
 }
 
 void App::onWatchdogTimeout (void)
diff --git a/framework/common/tcuApp.hpp b/framework/common/tcuApp.hpp
index 22e5606..8233eb3 100644
--- a/framework/common/tcuApp.hpp
+++ b/framework/common/tcuApp.hpp
@@ -37,12 +37,10 @@
 class Archive;
 class Platform;
 class TestContext;
-class TestExecutor;
+class TestSessionExecutor;
 class CommandLine;
 class TestLog;
-
-// Defined in tcuTestExecutor.hpp
-class TestRunResult;
+class TestPackageRoot;
 
 /*--------------------------------------------------------------------*//*!
  * \brief Test application
@@ -58,7 +56,7 @@
  * App is responsible of setting up crash handler (qpCrashHandler) and
  * watchdog (qpWatchDog).
  *
- * See tcuMain.cpp for example on how to implement application stub.
+ * See tcuMain.cpp for an example on how to implement application stub.
  *//*--------------------------------------------------------------------*/
 class App
 {
@@ -68,12 +66,15 @@
 
 	bool					iterate				(void);
 
-	const TestRunResult&	getResult			(void) const;
+protected:
+	void					cleanup				(void);
 
 	void					onWatchdogTimeout	(void);
 	void					onCrash				(void);
 
-protected:
+	static void				onWatchdogTimeout	(qpWatchDog* watchDog, void* userPtr);
+	static void				onCrash				(qpCrashHandler* crashHandler, void* userPtr);
+
 	Platform&				m_platform;
 	qpWatchDog*				m_watchDog;
 	qpCrashHandler*			m_crashHandler;
@@ -81,7 +82,8 @@
 	bool					m_crashed;
 
 	TestContext*			m_testCtx;
-	TestExecutor*			m_testExecutor;
+	TestPackageRoot*		m_testRoot;
+	TestSessionExecutor*	m_testExecutor;
 };
 
 } // tcu
diff --git a/framework/common/tcuResource.cpp b/framework/common/tcuResource.cpp
index 648798c..5ecc597 100644
--- a/framework/common/tcuResource.cpp
+++ b/framework/common/tcuResource.cpp
@@ -51,13 +51,6 @@
 	m_file = fopen(filename, "rb");
 	if (!m_file)
 		throw ResourceError("Failed to open file", filename, __FILE__, __LINE__);
-
-/*	{
-		FILE* log = fopen("resources.log", "a");
-		fprintf(log, "%s\n", filename);
-		fflush(log);
-		fclose(log);
-	}*/
 }
 
 FileResource::~FileResource ()
diff --git a/framework/common/tcuResultCollector.hpp b/framework/common/tcuResultCollector.hpp
index 6398f0a..d4af499 100644
--- a/framework/common/tcuResultCollector.hpp
+++ b/framework/common/tcuResultCollector.hpp
@@ -63,7 +63,6 @@
 	std::string		m_message;
 } DE_WARN_UNUSED_TYPE;
 
-
 } // tcu
 
 #endif // _TCURESULTCOLLECTOR_HPP
\ No newline at end of file
diff --git a/framework/common/tcuTestCase.hpp b/framework/common/tcuTestCase.hpp
index 9ce5271..4543ca7 100644
--- a/framework/common/tcuTestCase.hpp
+++ b/framework/common/tcuTestCase.hpp
@@ -176,6 +176,24 @@
 	virtual			~TestCase			(void);
 };
 
+class TestStatus
+{
+public:
+						TestStatus		(qpTestResult code, const std::string& description) : m_code(code), m_description(description) {}
+
+	bool				isComplete		(void) const { return getCode() != QP_TEST_RESULT_LAST;			}
+	qpTestResult		getCode			(void) const { DE_ASSERT(isComplete()); return m_code;			}
+	const std::string&	getDescription	(void) const { DE_ASSERT(isComplete()); return m_description;	}
+
+	static TestStatus	pass			(const std::string& description)	{ return TestStatus(QP_TEST_RESULT_PASS,	description);	}
+	static TestStatus	fail			(const std::string& description)	{ return TestStatus(QP_TEST_RESULT_FAIL,	description);	}
+	static TestStatus	incomplete		(void)								{ return TestStatus(QP_TEST_RESULT_LAST,	"");			}
+
+private:
+	qpTestResult		m_code;
+	std::string			m_description;
+} DE_WARN_UNUSED_TYPE;
+
 } // tcu
 
 #endif // _TCUTESTCASE_HPP
diff --git a/framework/common/tcuTestCaseWrapper.cpp b/framework/common/tcuTestCaseWrapper.cpp
deleted file mode 100644
index b8e2eb5..0000000
--- a/framework/common/tcuTestCaseWrapper.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*-------------------------------------------------------------------------
- * drawElements Quality Program Tester Core
- * ----------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief Test case wrapper for test execution.
- *//*--------------------------------------------------------------------*/
-
-#include "tcuTestCaseWrapper.hpp"
-#include "tcuTestLog.hpp"
-#include "deClock.h"
-
-namespace tcu
-{
-
-TestCaseWrapper::TestCaseWrapper (TestContext& testCtx)
-	: m_testCtx			(testCtx)
-	, m_testStartTime	(0)
-{
-}
-
-TestCaseWrapper::~TestCaseWrapper (void)
-{
-}
-
-bool TestCaseWrapper::initTestCase (TestCase* testCase)
-{
-	// Initialize test case.
-	TestLog&	log		= m_testCtx.getLog();
-	bool		success	= false;
-
-	// Record test start time.
-	m_testStartTime = deGetMicroseconds();
-
-	try
-	{
-		testCase->init();
-		success = true;
-	}
-	catch (const std::bad_alloc&)
-	{
-		DE_ASSERT(!success);
-		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init");
-		m_testCtx.setTerminateAfter(true);
-	}
-	catch (const tcu::TestException& e)
-	{
-		DE_ASSERT(!success);
-		m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
-		m_testCtx.setTerminateAfter(e.isFatal());
-		log << e;
-	}
-	catch (const tcu::Exception& e)
-	{
-		DE_ASSERT(!success);
-		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
-		log << e;
-	}
-
-	DE_ASSERT(success || m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
-
-	return success;
-}
-
-bool TestCaseWrapper::deinitTestCase (TestCase* testCase)
-{
-	bool deinitOk = false;
-
-	// De-init case.
-	try
-	{
-		testCase->deinit();
-		deinitOk = true;
-	}
-	catch (const tcu::Exception& e)
-	{
-		m_testCtx.getLog() << e
-						   << TestLog::Message << "Error in test case deinit, test program will terminate." << TestLog::EndMessage;
-	}
-
-	{
-		const deInt64 duration = deGetMicroseconds()-m_testStartTime;
-		m_testStartTime = 0;
-		m_testCtx.getLog() << TestLog::Integer("TestDuration", "Test case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
-	}
-
-	return deinitOk;
-}
-
-TestNode::IterateResult TestCaseWrapper::iterateTestCase (TestCase* testCase)
-{
-	// Iterate the sub-case.
-	TestLog&				log				= m_testCtx.getLog();
-	TestCase::IterateResult	iterateResult	= TestCase::STOP;
-
-	try
-	{
-		iterateResult = testCase->iterate();
-	}
-	catch (const std::bad_alloc&)
-	{
-		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test execution");
-		m_testCtx.setTerminateAfter(true);
-	}
-	catch (const tcu::TestException& e)
-	{
-		log << e;
-		m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
-		m_testCtx.setTerminateAfter(e.isFatal());
-	}
-	catch (const tcu::Exception& e)
-	{
-		log << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
-	}
-
-	return iterateResult;
-}
-
-} // tcu
diff --git a/framework/common/tcuTestCaseWrapper.hpp b/framework/common/tcuTestCaseWrapper.hpp
deleted file mode 100644
index 65bf406..0000000
--- a/framework/common/tcuTestCaseWrapper.hpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef _TCUTESTCASEWRAPPER_HPP
-#define _TCUTESTCASEWRAPPER_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program Tester Core
- * ----------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief Test case wrapper for test execution.
- *//*--------------------------------------------------------------------*/
-
-#include "tcuDefs.hpp"
-#include "tcuTestContext.hpp"
-#include "tcuTestCase.hpp"
-
-namespace tcu
-{
-
-class TestCaseWrapper
-{
-public:
-										TestCaseWrapper			(TestContext& testCtx);
-	virtual								~TestCaseWrapper		(void);
-
-	virtual bool						initTestCase			(TestCase* testCase);
-	virtual bool						deinitTestCase			(TestCase* testCase);
-
-	virtual TestNode::IterateResult		iterateTestCase			(TestCase* testCase);
-
-protected:
-	TestContext&						m_testCtx;
-
-	deUint64							m_testStartTime;		//!< For logging test case durations.
-};
-
-} // tcu
-
-#endif // _TCUTESTCASEWRAPPER_HPP
diff --git a/framework/common/tcuTestContext.hpp b/framework/common/tcuTestContext.hpp
index 3aa474d..2bf4ed3 100644
--- a/framework/common/tcuTestContext.hpp
+++ b/framework/common/tcuTestContext.hpp
@@ -69,7 +69,11 @@
 
 	void					setTerminateAfter	(bool terminate)	{ m_terminateAfter = terminate;	}
 	bool					getTerminateAfter	(void) const		{ return m_terminateAfter; 		}
+
 protected:
+							TestContext			(const TestContext&);
+	TestContext&			operator=			(const TestContext&);
+
 	Platform&				m_platform;			//!< Platform port implementation.
 	Archive&				m_rootArchive;		//!< Root archive.
 	TestLog&				m_log;				//!< Test log.
@@ -82,7 +86,6 @@
 	bool					m_terminateAfter;	//!< Should tester terminate after execution of the current test
 };
 
-
 } // tcu
 
 #endif // _TCUTESTCONTEXT_HPP
diff --git a/framework/common/tcuTestExecutor.cpp b/framework/common/tcuTestExecutor.cpp
deleted file mode 100644
index 69500a2..0000000
--- a/framework/common/tcuTestExecutor.cpp
+++ /dev/null
@@ -1,408 +0,0 @@
-/*-------------------------------------------------------------------------
- * drawElements Quality Program Tester Core
- * ----------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief Class for executing tests.
- *//*--------------------------------------------------------------------*/
-
-#include "tcuTestExecutor.hpp"
-#include "tcuCommandLine.hpp"
-#include "tcuPlatform.hpp"
-#include "tcuTestLog.hpp"
-
-#include "deInt32.h"
-
-#include <typeinfo>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-using std::string;
-using std::vector;
-
-namespace tcu
-{
-
-TestExecutor::TestExecutor (TestContext& testCtx, const CommandLine& cmdLine)
-	: m_testCtx				(testCtx)
-	, m_cmdLine				(cmdLine)
-	, m_rootNode			(DE_NULL)
-	, m_testCaseWrapper		(DE_NULL)
-	, m_testCaseListFile	(DE_NULL)
-	, m_testCaseListWriter	(DE_NULL)
-{
-	m_abortSession	= false;
-	m_isInTestCase	= false;
-
-	// Create the root node.
-	TestPackageRegistry*						packageRegistry	= TestPackageRegistry::getSingleton();
-	vector<TestPackageRegistry::PackageInfo*>	packageInfos	= packageRegistry->getPackageInfos();
-	vector<TestNode*>							testPackages;
-
-	for (int i = 0; i < (int)packageInfos.size(); i++)
-		testPackages.push_back(packageInfos[i]->createFunc(testCtx));
-
-	m_rootNode = new TestPackageRoot(testCtx, testPackages);
-
-	// Init traverse stack.
-	NodeIter iter(m_rootNode);
-	m_sessionStack.push_back(iter);
-}
-
-TestExecutor::~TestExecutor (void)
-{
-	if (m_testCaseListWriter)
-		qpXmlWriter_destroy(m_testCaseListWriter);
-
-	if (m_testCaseListFile)
-		fclose(m_testCaseListFile);
-
-	delete m_rootNode;
-}
-
-// Test sub-case iteration.
-void TestExecutor::enterTestPackage (TestPackage* testPackage, const char* packageName)
-{
-	DE_ASSERT(testPackage && packageName);
-
-	// Open file/writer for case dumping.
-	const RunMode runMode = m_cmdLine.getRunMode();
-	if (runMode == RUNMODE_DUMP_XML_CASELIST || runMode == RUNMODE_DUMP_TEXT_CASELIST)
-	{
-		const char* const	ext				= (runMode == RUNMODE_DUMP_XML_CASELIST) ? "xml" : "txt";
-		const string		fileName		= string(packageName) + "-cases." + ext;
-
-		print("Dumping all test case names in '%s' to file '%s'..\n", packageName, fileName.c_str());
-		TCU_CHECK(m_testCaseListFile = fopen(fileName.c_str(), "wb"));
-
-		if (runMode == RUNMODE_DUMP_XML_CASELIST)
-		{
-			TCU_CHECK(m_testCaseListWriter = qpXmlWriter_createFileWriter(m_testCaseListFile, DE_FALSE));
-
-			qpXmlWriter_startDocument(m_testCaseListWriter);
-			qpXmlWriter_startElement(m_testCaseListWriter, "TestCaseList", 0, DE_NULL);
-		}
-	}
-
-	// Initialize package.
-	testPackage->init();
-
-	// Store test case wrapper
-	m_testCaseWrapper = &testPackage->getTestCaseWrapper();
-	DE_ASSERT(m_testCaseWrapper);
-
-	// Set archive.
-	m_testCtx.setCurrentArchive(testPackage->getArchive());
-}
-
-void TestExecutor::leaveTestPackage (TestPackage* testPackage)
-{
-	DE_ASSERT(testPackage);
-
-	const RunMode runMode = m_cmdLine.getRunMode();
-	if (runMode == RUNMODE_DUMP_XML_CASELIST)
-	{
-		qpXmlWriter_endElement(m_testCaseListWriter, "TestCaseList");
-		qpXmlWriter_endDocument(m_testCaseListWriter);
-		qpXmlWriter_destroy(m_testCaseListWriter);
-		m_testCaseListWriter = DE_NULL;
-	}
-
-	if (runMode == RUNMODE_DUMP_TEXT_CASELIST || runMode == RUNMODE_DUMP_XML_CASELIST)
-	{
-		fclose(m_testCaseListFile);
-		m_testCaseListFile = DE_NULL;
-	}
-
-	DE_ASSERT(!m_testCaseListWriter && !m_testCaseListFile);
-
-	m_testCaseWrapper = DE_NULL;
-	m_testCtx.setCurrentArchive(m_testCtx.getRootArchive());
-
-	// Deinitialize package.
-	testPackage->deinit();
-}
-
-void TestExecutor::enterGroupNode (TestCaseGroup* testGroup, const char* casePath)
-{
-	DE_UNREF(casePath);
-	testGroup->init();
-}
-
-void TestExecutor::leaveGroupNode (TestCaseGroup* testGroup)
-{
-	testGroup->deinit();
-}
-
-static qpTestCaseType nodeTypeToTestCaseType (TestNodeType nodeType)
-{
-	switch (nodeType)
-	{
-		case NODETYPE_SELF_VALIDATE:	return QP_TEST_CASE_TYPE_SELF_VALIDATE;
-		case NODETYPE_PERFORMANCE:		return QP_TEST_CASE_TYPE_PERFORMANCE;
-		case NODETYPE_CAPABILITY:		return QP_TEST_CASE_TYPE_CAPABILITY;
-		case NODETYPE_ACCURACY:			return QP_TEST_CASE_TYPE_ACCURACY;
-		default:
-			DE_ASSERT(DE_FALSE);
-			return QP_TEST_CASE_TYPE_LAST;
-	}
-}
-
-bool TestExecutor::enterTestCase (TestCase* testCase, const char* casePath)
-{
-	const RunMode			runMode		= m_cmdLine.getRunMode();
-	const qpTestCaseType	caseType	= nodeTypeToTestCaseType(testCase->getNodeType());
-
-	if (runMode == RUNMODE_EXECUTE)
-	{
-		print("\nTest case '%s'..\n", casePath);
-
-		m_testCtx.getLog().startCase(casePath, caseType);
-		m_isInTestCase = true;
-		m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
-
-		if (!m_testCaseWrapper->initTestCase(testCase))
-		{
-			if (m_testCtx.getTestResult() == QP_TEST_RESULT_LAST)
-				m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Unexpected error in subcase init");
-			return false;
-		}
-	}
-
-	return true;
-}
-
-void TestExecutor::leaveTestCase (TestCase* testCase)
-{
-	const RunMode runMode = m_cmdLine.getRunMode();
-	if (runMode == RUNMODE_EXECUTE)
-	{
-		// De-init case.
-		const bool			deinitOk		= m_testCaseWrapper->deinitTestCase(testCase);
-		const qpTestResult	testResult		= m_testCtx.getTestResult();
-		const char* const	testResultDesc	= m_testCtx.getTestResultDesc();
-		const bool			terminateAfter	= m_testCtx.getTerminateAfter();
-		DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
-
-		m_isInTestCase = false;
-		m_testCtx.getLog().endCase(testResult, testResultDesc);
-
-		// Update statistics.
-		print("  %s (%s)\n", qpGetTestResultName(testResult), testResultDesc);
-
-		m_result.numExecuted += 1;
-		switch (testResult)
-		{
-			case QP_TEST_RESULT_PASS:					m_result.numPassed			+= 1;	break;
-			case QP_TEST_RESULT_NOT_SUPPORTED:			m_result.numNotSupported	+= 1;	break;
-			case QP_TEST_RESULT_QUALITY_WARNING:		m_result.numWarnings		+= 1;	break;
-			case QP_TEST_RESULT_COMPATIBILITY_WARNING:	m_result.numWarnings		+= 1;	break;
-			default:									m_result.numFailed			+= 1;	break;
-		}
-
-		// terminateAfter, Resource error or any error in deinit means that execution should end
-		if (terminateAfter || !deinitOk || testResult == QP_TEST_RESULT_RESOURCE_ERROR)
-			m_abortSession = true;
-
-		// \todo [2011-02-09 pyry] Disable watchdog temporarily?
-		if (m_testCtx.getWatchDog())
-			qpWatchDog_reset(m_testCtx.getWatchDog());
-	}
-}
-
-// Return true while session should still continue, false otherwise.
-bool TestExecutor::iterate (void)
-{
-	try
-	{
-		while (!m_sessionStack.empty())
-		{
-			// Get full path to node.
-			string nodePath = "";
-			for (int ndx = 0; ndx < (int)m_sessionStack.size(); ndx++)
-			{
-				NodeIter& iter = m_sessionStack[ndx];
-				if (ndx > 1) // ignore root package
-					nodePath += ".";
-				nodePath += iter.node->getName();
-			}
-
-			// Handle the node.
-			NodeIter& iter = m_sessionStack[m_sessionStack.size()-1];
-			DE_ASSERT(iter.node != DE_NULL);
-			TestNode*		node	= iter.node;
-			bool			isLeaf	= isTestNodeTypeExecutable(node->getNodeType());
-
-			switch (iter.getState())
-			{
-				case NodeIter::STATE_BEGIN:
-				{
-					// Return to parent if name doesn't match filter.
-					if (!(isLeaf ? m_cmdLine.checkTestCaseName(nodePath.c_str()) : m_cmdLine.checkTestGroupName(nodePath.c_str())))
-					{
-						m_sessionStack.pop_back();
-						break;
-					}
-
-					// Enter node.
-					bool enterOk = true;
-					switch (node->getNodeType())
-					{
-						case NODETYPE_ROOT:				/* nada */																	break;
-						case NODETYPE_PACKAGE:			enterTestPackage(static_cast<TestPackage*>(node), nodePath.c_str());		break;
-						case NODETYPE_GROUP:			enterGroupNode(static_cast<TestCaseGroup*>(node), nodePath.c_str());		break;
-						case NODETYPE_PERFORMANCE:
-						case NODETYPE_CAPABILITY:
-						case NODETYPE_ACCURACY:			/* fall-trough */
-						case NODETYPE_SELF_VALIDATE:	enterOk = enterTestCase(static_cast<TestCase*>(node), nodePath.c_str());	break;
-						default: DE_ASSERT(false);
-					}
-
-					if (m_cmdLine.getRunMode() == RUNMODE_EXECUTE)
-					{
-						if (isLeaf)
-						{
-							if (enterOk)
-								iter.setState(NodeIter::STATE_EXECUTE_TEST);
-							else
-								iter.setState(NodeIter::STATE_FINISH);
-						}
-						else
-						{
-							iter.setState(NodeIter::STATE_TRAVERSE_CHILDREN);
-						}
-					}
-					else if (m_cmdLine.getRunMode() == RUNMODE_DUMP_XML_CASELIST)
-					{
-						if (node->getNodeType() != NODETYPE_ROOT && node->getNodeType() != NODETYPE_PACKAGE)
-						{
-							string			caseName	= iter.node->getName();
-							string			description	= iter.node->getDescription();
-							qpXmlAttribute	attribs[8];
-							int				numAttribs = 0;
-							const char*		caseType	= DE_NULL;
-
-							switch (node->getNodeType())
-							{
-								case NODETYPE_SELF_VALIDATE:	caseType = "SelfValidate";	break;
-								case NODETYPE_CAPABILITY:		caseType = "Capability";	break;
-								case NODETYPE_ACCURACY:			caseType = "Accuracy";		break;
-								case NODETYPE_PERFORMANCE:		caseType = "Performance";	break;
-								default:						caseType = "TestGroup";		break;
-							}
-
-							attribs[numAttribs++] = qpSetStringAttrib("Name", caseName.c_str());
-							attribs[numAttribs++] = qpSetStringAttrib("CaseType", caseType);
-							attribs[numAttribs++] = qpSetStringAttrib("Description", description.c_str());
-							qpXmlWriter_startElement(m_testCaseListWriter, "TestCase", numAttribs, attribs);
-						}
-
-						iter.setState(isLeaf ? NodeIter::STATE_FINISH : NodeIter::STATE_TRAVERSE_CHILDREN);
-					}
-					else if (m_cmdLine.getRunMode() == RUNMODE_DUMP_TEXT_CASELIST)
-					{
-						// \note Case list file is not open until we are in test package.
-						if (isLeaf)
-							fprintf(m_testCaseListFile, "TEST: %s\n", nodePath.c_str());
-						else if (node->getNodeType() != NODETYPE_ROOT)
-							fprintf(m_testCaseListFile, "GROUP: %s\n", nodePath.c_str());
-						iter.setState(isLeaf ? NodeIter::STATE_FINISH : NodeIter::STATE_TRAVERSE_CHILDREN);
-					}
-
-					break;
-				}
-
-				case NodeIter::STATE_EXECUTE_TEST:
-				{
-					// Touch the watchdog.
-					m_testCtx.touchWatchdog();
-
-					// Iterate the sub-case.
-					TestCase::IterateResult iterateResult = m_testCaseWrapper->iterateTestCase(static_cast<TestCase*>(node));
-
-					if (iterateResult == TestCase::STOP)
-						iter.setState(NodeIter::STATE_FINISH);
-
-					return true; // return after each iteration (when another iteration follows).
-				}
-
-				case NodeIter::STATE_TRAVERSE_CHILDREN:
-				{
-					int numChildren = (int)iter.children.size();
-					if (++iter.curChildNdx < numChildren)
-					{
-						// Push child to stack.
-						TestNode* childNode = iter.children[iter.curChildNdx];
-						m_sessionStack.push_back(NodeIter(childNode));
-					}
-					else
-						iter.setState(NodeIter::STATE_FINISH);
-
-					break;
-				}
-
-				case NodeIter::STATE_FINISH:
-				{
-					if (m_cmdLine.getRunMode() == RUNMODE_DUMP_XML_CASELIST)
-					{
-						if (node->getNodeType() != NODETYPE_ROOT && node->getNodeType() != NODETYPE_PACKAGE)
-							qpXmlWriter_endElement(m_testCaseListWriter, "TestCase");
-					}
-
-					// Leave node.
-					switch (node->getNodeType())
-					{
-						case NODETYPE_ROOT:				/* nada */											break;
-						case NODETYPE_PACKAGE:			leaveTestPackage(static_cast<TestPackage*>(node));	break;
-						case NODETYPE_GROUP:			leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
-						case NODETYPE_ACCURACY:
-						case NODETYPE_CAPABILITY:
-						case NODETYPE_PERFORMANCE:		/* fall-thru */
-						case NODETYPE_SELF_VALIDATE:	leaveTestCase(static_cast<TestCase*>(node));		break;
-						default: DE_ASSERT(false);
-					}
-
-					m_sessionStack.pop_back();
-
-					// Return if execution should abort.
-					if (m_abortSession)
-						return false;
-
-					// Otherwise continue iterating.
-					break;
-				}
-
-				default:
-					DE_ASSERT(DE_FALSE);
-					break;
-			}
-		}
-	}
-	catch (const std::exception& e)
-	{
-		print("TestExecutor::iterateSession(): Caught unhandled %s: %s\n", typeid(e).name(), e.what());
-		throw;
-	}
-
-	m_result.isComplete = true;
-	return false;
-}
-
-} // tcu
diff --git a/framework/common/tcuTestExecutor.hpp b/framework/common/tcuTestExecutor.hpp
deleted file mode 100644
index 168c5c8..0000000
--- a/framework/common/tcuTestExecutor.hpp
+++ /dev/null
@@ -1,174 +0,0 @@
-#ifndef _TCUTESTEXECUTOR_HPP
-#define _TCUTESTEXECUTOR_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program Tester Core
- * ----------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief Base class for a test case.
- *//*--------------------------------------------------------------------*/
-
-#include "deDefs.h"
-#include "tcuTestContext.hpp"
-#include "tcuTestCase.hpp"
-#include "tcuTestPackage.hpp"
-#include "qpXmlWriter.h"
-
-#include <vector>
-
-namespace tcu
-{
-
-class CommandLine;
-
-//! Test run summary.
-class TestRunResult
-{
-public:
-	TestRunResult (void) { clear(); }
-
-	void clear (void)
-	{
-		numExecuted		= 0;
-		numPassed		= 0;
-		numFailed		= 0;
-		numNotSupported	= 0;
-		numWarnings		= 0;
-		isComplete		= false;
-	}
-
-	int		numExecuted;		//!< Total number of cases executed.
-	int		numPassed;			//!< Number of cases passed.
-	int		numFailed;			//!< Number of cases failed.
-	int		numNotSupported;	//!< Number of cases not supported.
-	int		numWarnings;		//!< Number of QualityWarning / CompatibilityWarning results.
-	bool	isComplete;			//!< Is run complete.
-};
-
-/*--------------------------------------------------------------------*//*!
- * \brief Test executor
- *
- * Test executor traverses TestNode hierarchy and executes the cases
- * included in current test case set. If no test case set is provided
- * all test cases in hierarchy are executed.
- *//*--------------------------------------------------------------------*/
-class TestExecutor
-{
-public:
-							TestExecutor		(TestContext& testCtx, const CommandLine& cmdLine);
-							~TestExecutor		(void);
-
-	bool					iterate				(void);
-
-	const TestRunResult&	getResult			(void) const { return m_result;			}
-
-	bool					isInTestCase		(void) const { return m_isInTestCase;	}
-
-private:
-	struct NodeIter
-	{
-		enum State
-		{
-			STATE_BEGIN = 0,
-			STATE_TRAVERSE_CHILDREN,
-			STATE_EXECUTE_TEST,
-			STATE_FINISH,
-
-			STATE_LAST
-		};
-
-		NodeIter (void)
-			: node			(DE_NULL)
-			, curChildNdx	(-1)
-			, m_state		(STATE_LAST)
-		{
-		}
-
-		NodeIter (TestNode* node_)
-			: node			(node_)
-			, curChildNdx	(-1)
-			, m_state		(STATE_BEGIN)
-		{
-		}
-
-		State getState (void) const
-		{
-			return m_state;
-		}
-
-		void setState (State newState)
-		{
-			switch (newState)
-			{
-				case STATE_TRAVERSE_CHILDREN:
-					node->getChildren(children);
-					curChildNdx = -1;
-					break;
-
-				default:
-					// nada
-					break;
-			}
-
-			m_state = newState;
-		}
-
-		TestNode*				node;
-		std::vector<TestNode*>	children;
-		int						curChildNdx;
-
-	private:
-		State					m_state;
-	};
-
-							TestExecutor		(const TestExecutor&);		// not allowed!
-	TestExecutor&			operator=			(const TestExecutor&);		// not allowed!
-
-	bool					matchFolderName		(const char* folderName) const;
-	bool					matchCaseName		(const char* caseName) const;
-
-	void					enterTestPackage	(TestPackage* testPackage, const char* packageName);
-	void					leaveTestPackage	(TestPackage* testPackage);
-
-	void					enterGroupNode		(TestCaseGroup* testGroup, const char* casePath);
-	void					leaveGroupNode		(TestCaseGroup* testGroup);
-
-	bool					enterTestCase		(TestCase* testCase, const char* casePath);
-	void					leaveTestCase		(TestCase* testCase);
-
-	// Member variables.
-	TestContext&			m_testCtx;
-	const CommandLine&		m_cmdLine;
-	TestPackageRoot*		m_rootNode;
-
-	TestCaseWrapper*		m_testCaseWrapper;
-
-	FILE*					m_testCaseListFile;
-	qpXmlWriter*			m_testCaseListWriter;
-
-	// Current session state.
-	std::vector<NodeIter>	m_sessionStack;
-	bool					m_abortSession;
-	bool					m_isInTestCase;
-
-	TestRunResult			m_result;
-};
-
-} // tcu
-
-#endif // _TCUTESTEXECUTOR_HPP
diff --git a/framework/common/tcuTestHierarchyIterator.cpp b/framework/common/tcuTestHierarchyIterator.cpp
new file mode 100644
index 0000000..b0a6084
--- /dev/null
+++ b/framework/common/tcuTestHierarchyIterator.cpp
@@ -0,0 +1,259 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program Tester Core
+ * ----------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test case hierarchy iterator.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestHierarchyIterator.hpp"
+#include "tcuCommandLine.hpp"
+
+namespace tcu
+{
+
+using std::string;
+using std::vector;
+
+// TestHierarchyInflater
+
+TestHierarchyInflater::TestHierarchyInflater (void)
+{
+}
+
+TestHierarchyInflater::~TestHierarchyInflater (void)
+{
+}
+
+// DefaultHierarchyInflater
+
+DefaultHierarchyInflater::DefaultHierarchyInflater (TestContext& testCtx)
+	: m_testCtx(testCtx)
+{
+}
+
+DefaultHierarchyInflater::~DefaultHierarchyInflater (void)
+{
+}
+
+void DefaultHierarchyInflater::enterTestPackage (TestPackage* testPackage, vector<TestNode*>& children)
+{
+	{
+		Archive* const	pkgArchive	= testPackage->getArchive();
+
+		if (pkgArchive)
+			m_testCtx.setCurrentArchive(*pkgArchive);
+		else
+			m_testCtx.setCurrentArchive(m_testCtx.getRootArchive());
+	}
+
+	testPackage->init();
+	testPackage->getChildren(children);
+}
+
+void DefaultHierarchyInflater::leaveTestPackage (TestPackage* testPackage)
+{
+	m_testCtx.setCurrentArchive(m_testCtx.getRootArchive());
+	testPackage->deinit();
+}
+
+void DefaultHierarchyInflater::enterGroupNode (TestCaseGroup* testGroup, vector<TestNode*>& children)
+{
+	testGroup->init();
+	testGroup->getChildren(children);
+}
+
+void DefaultHierarchyInflater::leaveGroupNode (TestCaseGroup* testGroup)
+{
+	testGroup->deinit();
+}
+
+// TestHierarchyIterator
+
+TestHierarchyIterator::TestHierarchyIterator (TestPackageRoot&			rootNode,
+											  TestHierarchyInflater&	inflater,
+											  const CommandLine&		cmdLine)
+	: m_inflater	(inflater)
+	, m_cmdLine		(cmdLine)
+{
+	// Init traverse state and "seek" to first reportable node.
+	NodeIter iter(&rootNode);
+	iter.setState(NodeIter::STATE_ENTER); // Root is never reported
+	m_sessionStack.push_back(iter);
+	next();
+}
+
+TestHierarchyIterator::~TestHierarchyIterator (void)
+{
+	// Tear down inflated nodes in m_sessionStack
+	for (vector<NodeIter>::reverse_iterator iter = m_sessionStack.rbegin(); iter != m_sessionStack.rend(); ++iter)
+	{
+		TestNode* const		node		= iter->node;
+		const TestNodeType	nodeType	= node->getNodeType();
+
+		switch (nodeType)
+		{
+			case NODETYPE_ROOT:		/* root is not de-initialized */								break;
+			case NODETYPE_PACKAGE:	m_inflater.leaveTestPackage(static_cast<TestPackage*>(node));	break;
+			case NODETYPE_GROUP:	m_inflater.leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
+			default:
+				break;
+		}
+	}
+}
+
+TestHierarchyIterator::State TestHierarchyIterator::getState (void) const
+{
+	if (!m_sessionStack.empty())
+	{
+		const NodeIter&	iter	= m_sessionStack.back();
+
+		DE_ASSERT(iter.getState() == NodeIter::STATE_ENTER ||
+				  iter.getState() == NodeIter::STATE_LEAVE);
+
+		return iter.getState() == NodeIter::STATE_ENTER ? STATE_ENTER_NODE : STATE_LEAVE_NODE;
+	}
+	else
+		return STATE_FINISHED;
+}
+
+TestNode* TestHierarchyIterator::getNode (void) const
+{
+	DE_ASSERT(getState() != STATE_FINISHED);
+	return m_sessionStack.back().node;
+}
+
+const std::string& TestHierarchyIterator::getNodePath (void) const
+{
+	DE_ASSERT(getState() != STATE_FINISHED);
+	return m_nodePath;
+}
+
+std::string TestHierarchyIterator::buildNodePath (const vector<NodeIter>& nodeStack)
+{
+	string nodePath;
+	for (size_t ndx = 1; ndx < nodeStack.size(); ndx++)
+	{
+		const NodeIter& iter = nodeStack[ndx];
+		if (ndx > 1) // ignore root package
+			nodePath += ".";
+		nodePath += iter.node->getName();
+	}
+	return nodePath;
+}
+
+void TestHierarchyIterator::next (void)
+{
+	while (!m_sessionStack.empty())
+	{
+		NodeIter&			iter		= m_sessionStack.back();
+		TestNode* const		node		= iter.node;
+		const bool			isLeaf		= isTestNodeTypeExecutable(node->getNodeType());
+
+		switch (iter.getState())
+		{
+			case NodeIter::STATE_INIT:
+			{
+				const std::string nodePath = buildNodePath(m_sessionStack);
+
+				// Return to parent if name doesn't match filter.
+				if (!(isLeaf ? m_cmdLine.checkTestCaseName(nodePath.c_str()) : m_cmdLine.checkTestGroupName(nodePath.c_str())))
+				{
+					m_sessionStack.pop_back();
+					break;
+				}
+
+				m_nodePath = nodePath;
+				iter.setState(NodeIter::STATE_ENTER);
+				return; // Yield enter event
+			}
+
+			case NodeIter::STATE_ENTER:
+			{
+				if (isLeaf)
+				{
+					iter.setState(NodeIter::STATE_LEAVE);
+					return; // Yield leave event
+				}
+				else
+				{
+					iter.setState(NodeIter::STATE_TRAVERSE_CHILDREN);
+					iter.children.clear();
+
+					switch (node->getNodeType())
+					{
+						case NODETYPE_ROOT:		static_cast<TestPackageRoot*>(node)->getChildren(iter.children);				break;
+						case NODETYPE_PACKAGE:	m_inflater.enterTestPackage(static_cast<TestPackage*>(node), iter.children);	break;
+						case NODETYPE_GROUP:	m_inflater.enterGroupNode(static_cast<TestCaseGroup*>(node), iter.children);	break;
+						default:
+							DE_ASSERT(false);
+					}
+				}
+
+				break;
+			}
+
+			case NodeIter::STATE_TRAVERSE_CHILDREN:
+			{
+				int numChildren = (int)iter.children.size();
+				if (++iter.curChildNdx < numChildren)
+				{
+					// Push child to stack.
+					TestNode* childNode = iter.children[iter.curChildNdx];
+					m_sessionStack.push_back(NodeIter(childNode));
+				}
+				else
+				{
+					iter.setState(NodeIter::STATE_LEAVE);
+					if (node->getNodeType() != NODETYPE_ROOT)
+						return; // Yield leave event
+				}
+
+				break;
+			}
+
+			case NodeIter::STATE_LEAVE:
+			{
+				// Leave node.
+				if (!isLeaf)
+				{
+					switch (node->getNodeType())
+					{
+						case NODETYPE_ROOT:		/* root is not de-initialized */								break;
+						case NODETYPE_PACKAGE:	m_inflater.leaveTestPackage(static_cast<TestPackage*>(node));	break;
+						case NODETYPE_GROUP:	m_inflater.leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
+						default:
+							DE_ASSERT(false);
+					}
+				}
+
+				m_sessionStack.pop_back();
+				m_nodePath = buildNodePath(m_sessionStack);
+				break;
+			}
+
+			default:
+				DE_ASSERT(false);
+				return;
+		}
+	}
+
+	DE_ASSERT(m_sessionStack.empty() && getState() == STATE_FINISHED);
+}
+
+} // tcu
diff --git a/framework/common/tcuTestHierarchyIterator.hpp b/framework/common/tcuTestHierarchyIterator.hpp
new file mode 100644
index 0000000..ea4a4ea
--- /dev/null
+++ b/framework/common/tcuTestHierarchyIterator.hpp
@@ -0,0 +1,202 @@
+#ifndef _TCUTESTHIERARCHYITERATOR_HPP
+#define _TCUTESTHIERARCHYITERATOR_HPP
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program Tester Core
+ * ----------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test case hierarchy iterator.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestContext.hpp"
+#include "tcuTestCase.hpp"
+#include "tcuTestPackage.hpp"
+
+#include <vector>
+
+namespace tcu
+{
+
+class CommandLine;
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test hierarchy inflater
+ *
+ * This interface is used by TestHierarchyIterator to materialize, and clean
+ * up, test hierarchy on-demand while walking through it.
+ *//*--------------------------------------------------------------------*/
+class TestHierarchyInflater
+{
+public:
+									TestHierarchyInflater	(void);
+
+	virtual void					enterTestPackage		(TestPackage* testPackage, std::vector<TestNode*>& children) = 0;
+	virtual void					leaveTestPackage		(TestPackage* testPackage) = 0;
+
+	virtual void					enterGroupNode			(TestCaseGroup* testGroup, std::vector<TestNode*>& children) = 0;
+	virtual void					leaveGroupNode			(TestCaseGroup* testGroup) = 0;
+
+protected:
+									~TestHierarchyInflater	(void);
+};
+
+// \todo [2015-02-26 pyry] Hierarchy traversal should not depend on TestContext
+class DefaultHierarchyInflater : public TestHierarchyInflater
+{
+public:
+									DefaultHierarchyInflater	(TestContext& testCtx);
+									~DefaultHierarchyInflater	(void);
+
+	virtual void					enterTestPackage			(TestPackage* testPackage, std::vector<TestNode*>& children);
+	virtual void					leaveTestPackage			(TestPackage* testPackage);
+
+	virtual void					enterGroupNode				(TestCaseGroup* testGroup, std::vector<TestNode*>& children);
+	virtual void					leaveGroupNode				(TestCaseGroup* testGroup);
+
+protected:
+	TestContext&					m_testCtx;
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test hierarchy iterator
+ *
+ * Test hierarchy iterator allows walking test case hierarchy in depth-first
+ * order. The walked sub-tree is limited by command line parameters.
+ *
+ * Iterator signals current state with getState(), which initally, and after
+ * each increment (next()) may report one of the following:
+ *
+ * STATE_ENTER_NODE: A test node has been entered to for the first time.
+ *   Node can be queried with getNode() and its full path with getNodePath().
+ *   For group nodes the iterator will next enter first matching child node.
+ *   For executable (test case) nodes STATE_LEAVE_NODE will always be reported
+ *   immediately after entering that node.
+ *
+ * STATE_LEAVE_NODE: Iterator is leaving a node. In case of group nodes this
+ *   means that all child nodes and their children have been processed. For
+ *   executable nodes the iterator will either move on to the next sibling,
+ *   or leave the parent group if the reported node was last child of that
+ *   group.
+ *
+ * Root node is never reported, but instead iteration will start on first
+ * matching test package node, if there is any.
+ *
+ * Test hierarchy is created on demand with help of TestHierarchyInflater.
+ * Upon entering a group node, after STATE_ENTER_NODE has been signaled,
+ * inflater is called to construct the list of child nodes for that group.
+ * Upon exiting a group node, before STATE_LEAVE_NODE is called, inflater
+ * is asked to clean up any resources by calling leaveGroupNode() or
+ * leaveTestPackage() depending on the type of the node.
+ *//*--------------------------------------------------------------------*/
+class TestHierarchyIterator
+{
+public:
+							TestHierarchyIterator	(TestPackageRoot& rootNode, TestHierarchyInflater& inflater, const CommandLine& cmdLine);
+							~TestHierarchyIterator	(void);
+
+	enum State
+	{
+		STATE_ENTER_NODE = 0,
+		STATE_LEAVE_NODE,
+		STATE_FINISHED,
+
+		STATE_LAST
+	};
+
+	State					getState				(void) const;
+
+	TestNode*				getNode					(void) const;
+	const std::string&		getNodePath				(void) const;
+
+	void					next					(void);
+
+private:
+	struct NodeIter
+	{
+		enum State
+		{
+			STATE_INIT = 0,
+			STATE_ENTER,
+			STATE_TRAVERSE_CHILDREN,
+			STATE_LEAVE,
+
+			STATE_LAST
+		};
+
+		NodeIter (void)
+			: node			(DE_NULL)
+			, curChildNdx	(-1)
+			, m_state		(STATE_LAST)
+		{
+		}
+
+		NodeIter (TestNode* node_)
+			: node			(node_)
+			, curChildNdx	(-1)
+			, m_state		(STATE_INIT)
+		{
+		}
+
+		State getState (void) const
+		{
+			return m_state;
+		}
+
+		void setState (State newState)
+		{
+			switch (newState)
+			{
+				case STATE_TRAVERSE_CHILDREN:
+					curChildNdx = -1;
+					break;
+
+				default:
+					break;
+			}
+
+			m_state = newState;
+		}
+
+		TestNode*				node;
+		std::vector<TestNode*>	children;
+		int						curChildNdx;
+
+	private:
+		State					m_state;
+	};
+
+							TestHierarchyIterator	(const TestHierarchyIterator&);		// not allowed!
+	TestHierarchyIterator&	operator=				(const TestHierarchyIterator&);		// not allowed!
+
+	bool					matchFolderName			(const std::string& folderName) const;
+	bool					matchCaseName			(const std::string& caseName) const;
+
+	static std::string		buildNodePath			(const std::vector<NodeIter>& nodeStack);
+
+	TestHierarchyInflater&	m_inflater;
+	const CommandLine&		m_cmdLine;
+
+	// Current session state.
+	std::vector<NodeIter>	m_sessionStack;
+	std::string				m_nodePath;
+};
+
+} // tcu
+
+#endif // _TCUTESTHIERARCHYITERATOR_HPP
diff --git a/framework/common/tcuTestHierarchyUtil.cpp b/framework/common/tcuTestHierarchyUtil.cpp
new file mode 100644
index 0000000..e8cd6c9
--- /dev/null
+++ b/framework/common/tcuTestHierarchyUtil.cpp
@@ -0,0 +1,196 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program Tester Core
+ * ----------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test hierarchy utilities.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestHierarchyUtil.hpp"
+#include "tcuStringTemplate.hpp"
+#include "qpXmlWriter.h"
+
+#include <fstream>
+
+namespace tcu
+{
+
+using std::string;
+
+static const char* getNodeTypeName (TestNodeType nodeType)
+{
+	switch (nodeType)
+	{
+		case NODETYPE_SELF_VALIDATE:	return "SelfValidate";
+		case NODETYPE_CAPABILITY:		return "Capability";
+		case NODETYPE_ACCURACY:			return "Accuracy";
+		case NODETYPE_PERFORMANCE:		return "Performance";
+		case NODETYPE_GROUP:			return "TestGroup";
+		default:
+			DE_ASSERT(false);
+			return DE_NULL;
+	}
+}
+
+// Utilities
+
+static std::string makePackageFilename (const std::string& pattern, const std::string& packageName, const std::string& typeExtension)
+{
+	std::map<string, string> args;
+	args["packageName"]		= packageName;
+	args["typeExtension"]	= typeExtension;
+	return StringTemplate(pattern).specialize(args);
+}
+
+void writeXmlCaselists (TestPackageRoot& root, TestContext& testCtx, const tcu::CommandLine& cmdLine)
+{
+	const  char* const			filenamePattern	= "${packageName}-cases.${typeExtension}";	// \todo [2015-02-27 pyry] Make this command line argument
+	DefaultHierarchyInflater	inflater		(testCtx);
+	TestHierarchyIterator		iter			(root, inflater, cmdLine);
+	FILE*						curFile			= DE_NULL;
+	qpXmlWriter*				writer			= DE_NULL;
+
+	try
+	{
+		while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
+		{
+			const TestNode* const	node		= iter.getNode();
+			const TestNodeType		nodeType	= node->getNodeType();
+			const bool				isEnter		= iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE;
+
+			DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE ||
+					  iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE);
+
+			if (nodeType == NODETYPE_PACKAGE)
+			{
+				if (isEnter)
+				{
+					const string	filename	= makePackageFilename(filenamePattern, node->getName(), "xml");
+					qpXmlAttribute	attribs[2];
+					int				numAttribs	= 0;
+
+					DE_ASSERT(!curFile && !writer);
+
+					print("Writing test cases from '%s' to file '%s'..\n", node->getName(), filename.c_str());
+
+					curFile = fopen(filename.c_str(), "wb");
+					if (!curFile)
+						throw Exception("Failed to open " + filename);
+
+					writer = qpXmlWriter_createFileWriter(curFile, DE_FALSE);
+					if (!writer)
+						throw Exception("Failed to create qpXmlWriter");
+
+					attribs[numAttribs++] = qpSetStringAttrib("PackageName",	node->getName());
+					attribs[numAttribs++] = qpSetStringAttrib("Description",	node->getDescription());
+					DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
+
+					if (!qpXmlWriter_startDocument(writer) ||
+						!qpXmlWriter_startElement(writer, "TestCaseList", numAttribs, attribs))
+						throw Exception("Failed to start XML document");
+				}
+				else
+				{
+					if (!qpXmlWriter_endElement(writer, "TestCaseList") ||
+						!qpXmlWriter_endDocument(writer))
+						throw Exception("Failed to terminate XML document");
+
+					qpXmlWriter_destroy(writer);
+					fclose(curFile);
+
+					writer	= DE_NULL;
+					curFile	= DE_NULL;
+				}
+			}
+			else
+			{
+				if (isEnter)
+				{
+					const string	caseName	= node->getName();
+					const string	description	= node->getDescription();
+					qpXmlAttribute	attribs[3];
+					int				numAttribs = 0;
+
+					attribs[numAttribs++] = qpSetStringAttrib("Name",			caseName.c_str());
+					attribs[numAttribs++] = qpSetStringAttrib("CaseType",		getNodeTypeName(nodeType));
+					attribs[numAttribs++] = qpSetStringAttrib("Description",	description.c_str());
+					DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
+
+					if (!qpXmlWriter_startElement(writer, "TestCase", numAttribs, attribs))
+						throw Exception("Writing to case list file failed");
+				}
+				else
+				{
+					if (!qpXmlWriter_endElement(writer, "TestCase"))
+						throw tcu::Exception("Writing to case list file failed");
+				}
+			}
+
+			iter.next();
+		}
+	}
+	catch (...)
+	{
+		if (writer)
+			qpXmlWriter_destroy(writer);
+
+		if (curFile)
+			fclose(curFile);
+
+		throw;
+	}
+
+	DE_ASSERT(!curFile && !writer);
+}
+
+void writeTxtCaselists (TestPackageRoot& root, TestContext& testCtx, const tcu::CommandLine& cmdLine)
+{
+	const  char* const			filenamePattern	= "${packageName}-cases.${typeExtension}";	// \todo [2015-02-27 pyry] Make this command line argument
+	DefaultHierarchyInflater	inflater		(testCtx);
+	TestHierarchyIterator		iter			(root, inflater, cmdLine);
+
+	while (iter.getState() != TestHierarchyIterator::STATE_FINISHED)
+	{
+		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE &&
+				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
+
+		const char*		pkgName		= iter.getNode()->getName();
+		const string	filename	= makePackageFilename(filenamePattern, pkgName, "txt");
+		std::ofstream	out			(filename.c_str(), std::ios_base::binary);
+
+		if (!out.is_open() || !out.good())
+			throw Exception("Failed to open " + filename);
+
+		print("Writing test cases from '%s' to file '%s'..\n", pkgName, filename.c_str());
+
+		iter.next();
+
+		while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE)
+		{
+			if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE)
+				out << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n";
+			iter.next();
+		}
+
+		DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
+				  iter.getNode()->getNodeType() == NODETYPE_PACKAGE);
+		iter.next();
+	}
+}
+
+} // tcu
diff --git a/framework/common/tcuTestHierarchyUtil.hpp b/framework/common/tcuTestHierarchyUtil.hpp
new file mode 100644
index 0000000..683f7f1
--- /dev/null
+++ b/framework/common/tcuTestHierarchyUtil.hpp
@@ -0,0 +1,38 @@
+#ifndef _TCUTESTHIERARCHYUTIL_HPP
+#define _TCUTESTHIERARCHYUTIL_HPP
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program Tester Core
+ * ----------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test hierarchy utilities.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestHierarchyIterator.hpp"
+
+namespace tcu
+{
+
+// \todo [2015-02-26 pyry] Remove TestContext requirement
+void	writeXmlCaselists	(TestPackageRoot& root, TestContext& testCtx, const tcu::CommandLine& cmdLine);
+void	writeTxtCaselists	(TestPackageRoot& root, TestContext& testCtx, const tcu::CommandLine& cmdLine);
+
+} // tcu
+
+#endif // _TCUTESTHIERARCHYUTIL_HPP
diff --git a/framework/common/tcuTestPackage.cpp b/framework/common/tcuTestPackage.cpp
index 8ab2580..047acc9 100644
--- a/framework/common/tcuTestPackage.cpp
+++ b/framework/common/tcuTestPackage.cpp
@@ -144,6 +144,15 @@
 {
 }
 
+TestPackageRoot::TestPackageRoot (TestContext& testCtx, const TestPackageRegistry* packageRegistry)
+	: TestNode(testCtx, NODETYPE_ROOT, "", "")
+{
+	const vector<TestPackageRegistry::PackageInfo*>&	packageInfos	= packageRegistry->getPackageInfos();
+
+	for (int i = 0; i < (int)packageInfos.size(); i++)
+		addChild(packageInfos[i]->createFunc(testCtx));
+}
+
 TestPackageRoot::~TestPackageRoot (void)
 {
 }
diff --git a/framework/common/tcuTestPackage.hpp b/framework/common/tcuTestPackage.hpp
index 2956316..2e7aa74 100644
--- a/framework/common/tcuTestPackage.hpp
+++ b/framework/common/tcuTestPackage.hpp
@@ -25,31 +25,59 @@
 
 #include "tcuDefs.hpp"
 #include "tcuTestCase.hpp"
-#include "tcuTestCaseWrapper.hpp"
 
 namespace tcu
 {
 
 /*--------------------------------------------------------------------*//*!
+ * \brief Test case execution interface.
+ *
+ * TestCaseExecutor provides package-specific resources & initialization
+ * for test cases.
+ *
+ * \todo [2015-03-18 pyry] Replace with following API:
+ *
+ * class TestInstance
+ * {
+ * public:
+ *     TestInstance (TestContext& testCtx);
+ *     tcu::TestResult iterate (void);
+ * };
+ *
+ * class TestInstanceFactory (???)
+ * {
+ * public:
+ *     TestInstance* createInstance (const TestCase* testCase, const std::string& path);
+ * };
+ *//*--------------------------------------------------------------------*/
+class TestCaseExecutor
+{
+public:
+	virtual								~TestCaseExecutor	(void) {}
+
+	virtual void						init				(TestCase* testCase, const std::string& path) = 0;
+	virtual void						deinit				(TestCase* testCase) = 0;
+	virtual TestNode::IterateResult		iterate				(TestCase* testCase) = 0;
+};
+
+/*--------------------------------------------------------------------*//*!
  * \brief Base class for test packages.
  *
- * Test packages are root-level test groups. Test case exposes couple of
- * extra customization points. Test package can define custom TestCaseWrapper
- * and archive (usually ResourcePrefix around default archive) for resources.
- *
- * Test package is typically responsible of setting up rendering context
- * for test cases.
+ * Test packages are root-level test groups. They also provide package-
+ * specific test case executor, see TestCaseExecutor.
  *//*--------------------------------------------------------------------*/
 class TestPackage : public TestNode
 {
 public:
-								TestPackage			(TestContext& testCtx, const char* name, const char* description);
-	virtual						~TestPackage		(void);
+									TestPackage			(TestContext& testCtx, const char* name, const char* description);
+	virtual							~TestPackage		(void);
 
-	virtual IterateResult		iterate				(void);
+	virtual TestCaseExecutor*		createExecutor		(void) const = 0;
 
-	virtual TestCaseWrapper&	getTestCaseWrapper	(void) = DE_NULL;
-	virtual Archive&			getArchive			(void) = DE_NULL;
+	// Deprecated
+	virtual Archive*				getArchive			(void) { return DE_NULL; }
+
+	virtual IterateResult			iterate				(void);
 };
 
 // TestPackageRegistry
@@ -101,6 +129,7 @@
 public:
 							TestPackageRoot		(TestContext& testCtx);
 							TestPackageRoot		(TestContext& testCtx, const std::vector<TestNode*>& children);
+							TestPackageRoot		(TestContext& testCtx, const TestPackageRegistry* packageRegistry);
 	virtual					~TestPackageRoot	(void);
 
 	virtual IterateResult	iterate				(void);
diff --git a/framework/common/tcuTestSessionExecutor.cpp b/framework/common/tcuTestSessionExecutor.cpp
new file mode 100644
index 0000000..630f22c
--- /dev/null
+++ b/framework/common/tcuTestSessionExecutor.cpp
@@ -0,0 +1,292 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program Tester Core
+ * ----------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test executor.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuTestSessionExecutor.hpp"
+#include "tcuTestLog.hpp"
+
+#include "deClock.h"
+
+namespace tcu
+{
+
+using std::vector;
+
+static qpTestCaseType nodeTypeToTestCaseType (TestNodeType nodeType)
+{
+	switch (nodeType)
+	{
+		case NODETYPE_SELF_VALIDATE:	return QP_TEST_CASE_TYPE_SELF_VALIDATE;
+		case NODETYPE_PERFORMANCE:		return QP_TEST_CASE_TYPE_PERFORMANCE;
+		case NODETYPE_CAPABILITY:		return QP_TEST_CASE_TYPE_CAPABILITY;
+		case NODETYPE_ACCURACY:			return QP_TEST_CASE_TYPE_ACCURACY;
+		default:
+			DE_ASSERT(false);
+			return QP_TEST_CASE_TYPE_LAST;
+	}
+}
+
+TestSessionExecutor::TestSessionExecutor (TestPackageRoot& root, TestContext& testCtx)
+	: m_testCtx			(testCtx)
+	, m_inflater		(testCtx)
+	, m_iterator		(root, m_inflater, testCtx.getCommandLine())
+	, m_state			(STATE_TRAVERSE_HIERARCHY)
+	, m_abortSession	(false)
+	, m_isInTestCase	(false)
+	, m_testStartTime	(0)
+{
+}
+
+TestSessionExecutor::~TestSessionExecutor (void)
+{
+}
+
+bool TestSessionExecutor::iterate (void)
+{
+	for (;;)
+	{
+		switch (m_state)
+		{
+			case STATE_TRAVERSE_HIERARCHY:
+			{
+				const TestHierarchyIterator::State	hierIterState	= m_iterator.getState();
+
+				if (hierIterState == TestHierarchyIterator::STATE_ENTER_NODE ||
+					hierIterState == TestHierarchyIterator::STATE_LEAVE_NODE)
+				{
+					TestNode* const		curNode		= m_iterator.getNode();
+					const TestNodeType	nodeType	= curNode->getNodeType();
+					const bool			isEnter		= hierIterState == TestHierarchyIterator::STATE_ENTER_NODE;
+
+					switch (nodeType)
+					{
+						case NODETYPE_PACKAGE:
+						{
+							TestPackage* const testPackage = static_cast<TestPackage*>(curNode);
+							isEnter ? enterTestPackage(testPackage) : leaveTestPackage(testPackage);
+							break;
+						}
+
+						case NODETYPE_GROUP:
+							break; // nada
+
+						case NODETYPE_SELF_VALIDATE:
+						case NODETYPE_PERFORMANCE:
+						case NODETYPE_CAPABILITY:
+						case NODETYPE_ACCURACY:
+						{
+							TestCase* const testCase = static_cast<TestCase*>(curNode);
+
+							if (isEnter)
+							{
+								if (enterTestCase(testCase, m_iterator.getNodePath()))
+									m_state = STATE_EXECUTE_TEST_CASE;
+								// else remain in TRAVERSING_HIERARCHY => node will be exited from in the next iteration
+							}
+							else
+								leaveTestCase(testCase);
+
+							break;
+						}
+
+						default:
+							DE_ASSERT(false);
+							break;
+					}
+
+					m_iterator.next();
+					break;
+				}
+				else
+				{
+					DE_ASSERT(hierIterState == TestHierarchyIterator::STATE_FINISHED);
+					return false;
+				}
+			}
+
+			case STATE_EXECUTE_TEST_CASE:
+			{
+				DE_ASSERT(m_iterator.getState() == TestHierarchyIterator::STATE_LEAVE_NODE &&
+						  isTestNodeTypeExecutable(m_iterator.getNode()->getNodeType()));
+
+				TestCase* const					testCase	= static_cast<TestCase*>(m_iterator.getNode());
+				const TestCase::IterateResult	iterResult	= iterateTestCase(testCase);
+
+				if (iterResult == TestCase::STOP)
+					m_state = STATE_TRAVERSE_HIERARCHY;
+
+				return true;
+			}
+
+			default:
+				DE_ASSERT(false);
+				break;
+		}
+	}
+
+	DE_ASSERT(false);
+	return false;
+}
+
+void TestSessionExecutor::enterTestPackage (TestPackage* testPackage)
+{
+	// Create test case wrapper
+	DE_ASSERT(!m_caseExecutor);
+	m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor());
+}
+
+void TestSessionExecutor::leaveTestPackage (TestPackage* testPackage)
+{
+	DE_UNREF(testPackage);
+	m_caseExecutor.clear();
+}
+
+bool TestSessionExecutor::enterTestCase (TestCase* testCase, const std::string& casePath)
+{
+	TestLog&				log			= m_testCtx.getLog();
+	const qpTestCaseType	caseType	= nodeTypeToTestCaseType(testCase->getNodeType());
+	bool					initOk		= false;
+
+	print("\nTest case '%s'..\n", casePath.c_str());
+
+	m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
+	m_testCtx.setTerminateAfter(false);
+	log.startCase(casePath.c_str(), caseType);
+
+	m_isInTestCase	= true;
+	m_testStartTime	= deGetMicroseconds();
+
+	try
+	{
+		m_caseExecutor->init(testCase, casePath);
+		initOk = true;
+	}
+	catch (const std::bad_alloc&)
+	{
+		DE_ASSERT(!initOk);
+		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init");
+		m_testCtx.setTerminateAfter(true);
+	}
+	catch (const tcu::TestException& e)
+	{
+		DE_ASSERT(!initOk);
+		DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST);
+		m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
+		m_testCtx.setTerminateAfter(e.isFatal());
+		log << e;
+	}
+	catch (const tcu::Exception& e)
+	{
+		DE_ASSERT(!initOk);
+		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
+		log << e;
+	}
+
+	DE_ASSERT(initOk || m_testCtx.getTestResult() != QP_TEST_RESULT_LAST);
+
+	return initOk;
+}
+
+void TestSessionExecutor::leaveTestCase (TestCase* testCase)
+{
+	TestLog&	log		= m_testCtx.getLog();
+
+	// De-init case.
+	try
+	{
+		m_caseExecutor->deinit(testCase);
+	}
+	catch (const tcu::Exception& e)
+	{
+		log << e << TestLog::Message << "Error in test case deinit, test program will terminate." << TestLog::EndMessage;
+		m_testCtx.setTerminateAfter(true);
+	}
+
+	{
+		const deInt64 duration = deGetMicroseconds()-m_testStartTime;
+		m_testStartTime = 0;
+		m_testCtx.getLog() << TestLog::Integer("TestDuration", "Test case duration in microseconds", "us", QP_KEY_TAG_TIME, duration);
+	}
+
+	{
+		const qpTestResult	testResult		= m_testCtx.getTestResult();
+		const char* const	testResultDesc	= m_testCtx.getTestResultDesc();
+		const bool			terminateAfter	= m_testCtx.getTerminateAfter();
+		DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
+
+		m_isInTestCase = false;
+		m_testCtx.getLog().endCase(testResult, testResultDesc);
+
+		// Update statistics.
+		print("  %s (%s)\n", qpGetTestResultName(testResult), testResultDesc);
+
+		m_status.numExecuted += 1;
+		switch (testResult)
+		{
+			case QP_TEST_RESULT_PASS:					m_status.numPassed			+= 1;	break;
+			case QP_TEST_RESULT_NOT_SUPPORTED:			m_status.numNotSupported	+= 1;	break;
+			case QP_TEST_RESULT_QUALITY_WARNING:		m_status.numWarnings		+= 1;	break;
+			case QP_TEST_RESULT_COMPATIBILITY_WARNING:	m_status.numWarnings		+= 1;	break;
+			default:									m_status.numFailed			+= 1;	break;
+		}
+
+		// terminateAfter, Resource error or any error in deinit means that execution should end
+		if (terminateAfter || testResult == QP_TEST_RESULT_RESOURCE_ERROR)
+			m_abortSession = true;
+	}
+
+	if (m_testCtx.getWatchDog())
+		qpWatchDog_reset(m_testCtx.getWatchDog());
+}
+
+TestCase::IterateResult TestSessionExecutor::iterateTestCase (TestCase* testCase)
+{
+	TestLog&				log				= m_testCtx.getLog();
+	TestCase::IterateResult	iterateResult	= TestCase::STOP;
+
+	m_testCtx.touchWatchdog();
+
+	try
+	{
+		iterateResult = m_caseExecutor->iterate(testCase);
+	}
+	catch (const std::bad_alloc&)
+	{
+		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test execution");
+		m_testCtx.setTerminateAfter(true);
+	}
+	catch (const tcu::TestException& e)
+	{
+		log << e;
+		m_testCtx.setTestResult(e.getTestResult(), e.getMessage());
+		m_testCtx.setTerminateAfter(e.isFatal());
+	}
+	catch (const tcu::Exception& e)
+	{
+		log << e;
+		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage());
+	}
+
+	return iterateResult;
+}
+
+} // tcu
diff --git a/framework/common/tcuTestSessionExecutor.hpp b/framework/common/tcuTestSessionExecutor.hpp
new file mode 100644
index 0000000..ba5172f
--- /dev/null
+++ b/framework/common/tcuTestSessionExecutor.hpp
@@ -0,0 +1,102 @@
+#ifndef _TCUTESTSESSIONEXECUTOR_HPP
+#define _TCUTESTSESSIONEXECUTOR_HPP
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program Tester Core
+ * ----------------------------------------
+ *
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test executor.
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestContext.hpp"
+#include "tcuTestCase.hpp"
+#include "tcuTestPackage.hpp"
+#include "tcuTestHierarchyIterator.hpp"
+#include "deUniquePtr.hpp"
+
+namespace tcu
+{
+
+//! Test run summary.
+class TestRunStatus
+{
+public:
+	TestRunStatus (void) { clear(); }
+
+	void clear (void)
+	{
+		numExecuted		= 0;
+		numPassed		= 0;
+		numFailed		= 0;
+		numNotSupported	= 0;
+		numWarnings		= 0;
+		isComplete		= false;
+	}
+
+	int		numExecuted;		//!< Total number of cases executed.
+	int		numPassed;			//!< Number of cases passed.
+	int		numFailed;			//!< Number of cases failed.
+	int		numNotSupported;	//!< Number of cases not supported.
+	int		numWarnings;		//!< Number of QualityWarning / CompatibilityWarning results.
+	bool	isComplete;			//!< Is run complete.
+};
+
+class TestSessionExecutor
+{
+public:
+									TestSessionExecutor	(TestPackageRoot& root, TestContext& testCtx);
+									~TestSessionExecutor(void);
+
+	bool							iterate				(void);
+
+	bool							isInTestCase		(void) const { return m_isInTestCase;	}
+	const TestRunStatus&			getStatus			(void) const { return m_status;			}
+
+private:
+	void							enterTestPackage	(TestPackage* testPackage);
+	void							leaveTestPackage	(TestPackage* testPackage);
+
+	bool							enterTestCase		(TestCase* testCase, const std::string& casePath);
+	TestCase::IterateResult			iterateTestCase		(TestCase* testCase);
+	void							leaveTestCase		(TestCase* testCase);
+
+	enum State
+	{
+		STATE_TRAVERSE_HIERARCHY = 0,
+		STATE_EXECUTE_TEST_CASE,
+
+		STATE_LAST
+	};
+
+	TestContext&					m_testCtx;
+
+	DefaultHierarchyInflater		m_inflater;
+	TestHierarchyIterator			m_iterator;
+
+	de::MovePtr<TestCaseExecutor>	m_caseExecutor;
+	TestRunStatus					m_status;
+	State							m_state;
+	bool							m_abortSession;
+	bool							m_isInTestCase;
+	deUint64						m_testStartTime;
+};
+
+} // tcu
+
+#endif // _TCUTESTSESSIONEXECUTOR_HPP
diff --git a/modules/egl/teglTestPackage.cpp b/modules/egl/teglTestPackage.cpp
index 1602778..1336e14 100644
--- a/modules/egl/teglTestPackage.cpp
+++ b/modules/egl/teglTestPackage.cpp
@@ -127,29 +127,32 @@
 	}
 };
 
-TestCaseWrapper::TestCaseWrapper (EglTestContext& eglTestCtx)
-	: tcu::TestCaseWrapper(eglTestCtx.getTestContext())
+class TestCaseWrapper : public tcu::TestCaseExecutor
 {
-}
+public:
+	TestCaseWrapper (void)
+	{
+	}
 
-TestCaseWrapper::~TestCaseWrapper (void)
-{
-}
+	~TestCaseWrapper (void)
+	{
+	}
 
-bool TestCaseWrapper::initTestCase (tcu::TestCase* testCase)
-{
-	return tcu::TestCaseWrapper::initTestCase(testCase);
-}
+	void init (tcu::TestCase* testCase, const std::string&)
+	{
+		testCase->init();
+	}
 
-bool TestCaseWrapper::deinitTestCase (tcu::TestCase* testCase)
-{
-	return tcu::TestCaseWrapper::deinitTestCase(testCase);
-}
+	void deinit (tcu::TestCase* testCase)
+	{
+		testCase->deinit();
+	}
 
-tcu::TestNode::IterateResult TestCaseWrapper::iterateTestCase (tcu::TestCase* testCase)
-{
-	return tcu::TestCaseWrapper::iterateTestCase(testCase);
-}
+	tcu::TestNode::IterateResult iterate (tcu::TestCase* testCase)
+	{
+		return testCase->iterate();
+	}
+};
 
 static const eglu::NativeDisplayFactory& getDefaultDisplayFactory (tcu::TestContext& testCtx)
 {
@@ -158,20 +161,9 @@
 	return factory;
 }
 
-PackageContext::PackageContext (tcu::TestContext& testCtx)
-	: m_eglTestCtx	(testCtx, getDefaultDisplayFactory(testCtx))
-	, m_caseWrapper	(m_eglTestCtx)
-{
-}
-
-PackageContext::~PackageContext (void)
-{
-}
-
 TestPackage::TestPackage (tcu::TestContext& testCtx)
 	: tcu::TestPackage	(testCtx, "dEQP-EGL", "dEQP EGL Tests")
-	, m_packageCtx		(DE_NULL)
-	, m_archive			(testCtx.getRootArchive(), "egl/")
+	, m_eglTestCtx		(DE_NULL)
 {
 }
 
@@ -179,25 +171,25 @@
 {
 	// Destroy children first since destructors may access context.
 	TestNode::deinit();
-	delete m_packageCtx;
+	delete m_eglTestCtx;
 }
 
 void TestPackage::init (void)
 {
-	DE_ASSERT(!m_packageCtx);
-	m_packageCtx = new PackageContext(m_testCtx);
+	DE_ASSERT(!m_eglTestCtx);
+	m_eglTestCtx = new EglTestContext(m_testCtx, getDefaultDisplayFactory(m_testCtx));
 
 	try
 	{
-		addChild(new InfoTests			(m_packageCtx->getEglTestContext()));
-		addChild(new FunctionalTests	(m_packageCtx->getEglTestContext()));
-		addChild(new PerformanceTests	(m_packageCtx->getEglTestContext()));
-		addChild(new StressTests		(m_packageCtx->getEglTestContext()));
+		addChild(new InfoTests			(*m_eglTestCtx));
+		addChild(new FunctionalTests	(*m_eglTestCtx));
+		addChild(new PerformanceTests	(*m_eglTestCtx));
+		addChild(new StressTests		(*m_eglTestCtx));
 	}
 	catch (...)
 	{
-		delete m_packageCtx;
-		m_packageCtx = DE_NULL;
+		delete m_eglTestCtx;
+		m_eglTestCtx = DE_NULL;
 
 		throw;
 	}
@@ -206,8 +198,13 @@
 void TestPackage::deinit (void)
 {
 	tcu::TestNode::deinit();
-	delete m_packageCtx;
-	m_packageCtx = DE_NULL;
+	delete m_eglTestCtx;
+	m_eglTestCtx = DE_NULL;
+}
+
+tcu::TestCaseExecutor* TestPackage::createExecutor (void) const
+{
+	return new TestCaseWrapper();
 }
 
 } // egl
diff --git a/modules/egl/teglTestPackage.hpp b/modules/egl/teglTestPackage.hpp
index a6b1073..c9f0db2 100644
--- a/modules/egl/teglTestPackage.hpp
+++ b/modules/egl/teglTestPackage.hpp
@@ -26,39 +26,12 @@
 #include "tcuDefs.hpp"
 #include "tcuTestPackage.hpp"
 #include "teglTestCase.hpp"
-#include "tcuResource.hpp"
 
 namespace deqp
 {
 namespace egl
 {
 
-class TestCaseWrapper : public tcu::TestCaseWrapper
-{
-public:
-									TestCaseWrapper			(EglTestContext& eglTestContext);
-									~TestCaseWrapper		(void);
-
-	bool							initTestCase			(tcu::TestCase* testCase);
-	bool							deinitTestCase			(tcu::TestCase* testCase);
-
-	tcu::TestNode::IterateResult	iterateTestCase			(tcu::TestCase* testCase);
-};
-
-class PackageContext
-{
-public:
-									PackageContext			(tcu::TestContext& testCtx);
-									~PackageContext			(void);
-
-	EglTestContext&					getEglTestContext		(void) { return m_eglTestCtx;	}
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return m_caseWrapper;	}
-
-private:
-	EglTestContext					m_eglTestCtx;
-	TestCaseWrapper					m_caseWrapper;
-};
-
 class TestPackage : public tcu::TestPackage
 {
 public:
@@ -68,12 +41,10 @@
 	virtual void					init					(void);
 	virtual void					deinit					(void);
 
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return m_packageCtx->getTestCaseWrapper(); }
-	tcu::ResourcePrefix&			getArchive				(void) { return m_archive; }
+	tcu::TestCaseExecutor*			createExecutor			(void) const;
 
 private:
-	PackageContext*					m_packageCtx;
-	tcu::ResourcePrefix				m_archive;
+	EglTestContext*					m_eglTestCtx;
 };
 
 } // egl
diff --git a/modules/gles2/CMakeLists.txt b/modules/gles2/CMakeLists.txt
index 053f03b..e23cb02 100644
--- a/modules/gles2/CMakeLists.txt
+++ b/modules/gles2/CMakeLists.txt
@@ -24,8 +24,6 @@
 	tes2InfoTests.hpp
 	tes2TestCase.cpp
 	tes2TestCase.hpp
-	tes2TestCaseWrapper.cpp
-	tes2TestCaseWrapper.hpp
 	tes2TestPackage.cpp
 	tes2TestPackage.hpp
 	)
diff --git a/modules/gles2/tes2TestCaseWrapper.cpp b/modules/gles2/tes2TestCaseWrapper.cpp
deleted file mode 100644
index dc3fa2c..0000000
--- a/modules/gles2/tes2TestCaseWrapper.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES 2.0 Module
- * -------------------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief OpenGL ES 2.0 Test Case Wrapper.
- *//*--------------------------------------------------------------------*/
-
-#include "tes2TestCaseWrapper.hpp"
-#include "tcuTestLog.hpp"
-#include "gluDefs.hpp"
-#include "gluStateReset.hpp"
-#include "glwEnums.hpp"
-#include "glwFunctions.hpp"
-
-namespace deqp
-{
-namespace gles2
-{
-
-using tcu::TestLog;
-
-TestCaseWrapper::TestCaseWrapper (tcu::TestContext& testCtx, glu::RenderContext& renderCtx)
-	: tcu::TestCaseWrapper	(testCtx)
-	, m_renderCtx			(renderCtx)
-{
-	TCU_CHECK(contextSupports(renderCtx.getType(), glu::ApiType::es(2,0)));
-}
-
-TestCaseWrapper::~TestCaseWrapper (void)
-{
-}
-
-bool TestCaseWrapper::initTestCase (tcu::TestCase* testCase)
-{
-	return tcu::TestCaseWrapper::initTestCase(testCase);
-}
-
-bool TestCaseWrapper::deinitTestCase (tcu::TestCase* testCase)
-{
-	TestLog& log = m_testCtx.getLog();
-
-	if (!tcu::TestCaseWrapper::deinitTestCase(testCase))
-		return false;
-
-	try
-	{
-		// Reset state
-		glu::resetState(m_renderCtx);
-	}
-	catch (const std::exception& e)
-	{
-		log << e;
-		log << TestLog::Message << "Error in state reset, test program will terminate." << TestLog::EndMessage;
-		return false;
-	}
-
-	return true;
-}
-
-tcu::TestNode::IterateResult TestCaseWrapper::iterateTestCase (tcu::TestCase* testCase)
-{
-	TestLog&						log		= m_testCtx.getLog();
-	const glw::Functions&			gl		= m_renderCtx.getFunctions();
-	tcu::TestCase::IterateResult	result	= tcu::TestNode::STOP;
-
-	// Clear to surrender-blue
-	gl.clearColor(0.125f, 0.25f, 0.5f, 1.f);
-	gl.clear(GL_COLOR_BUFFER_BIT);
-
-	result = tcu::TestCaseWrapper::iterateTestCase(testCase);
-
-	// Call implementation specific post-iterate routine (usually handles native events and swaps buffers)
-	try
-	{
-		m_renderCtx.postIterate();
-		return result;
-	}
-	catch (const tcu::ResourceError& e)
-	{
-		m_testCtx.getLog() << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Resource error in context post-iteration routine");
-		m_testCtx.setTerminateAfter(true);
-		return tcu::TestNode::STOP;
-	}
-	catch (const std::exception& e)
-	{
-		log << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Error in context post-iteration routine");
-		return tcu::TestNode::STOP;
-	}
-}
-
-} // gles2
-} // deqp
diff --git a/modules/gles2/tes2TestCaseWrapper.hpp b/modules/gles2/tes2TestCaseWrapper.hpp
deleted file mode 100644
index 3f96070..0000000
--- a/modules/gles2/tes2TestCaseWrapper.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef _TES2TESTCASEWRAPPER_HPP
-#define _TES2TESTCASEWRAPPER_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES 2.0 Module
- * -------------------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief OpenGL ES 2.0 Test Case Wrapper.
- *//*--------------------------------------------------------------------*/
-
-#include "tcuDefs.hpp"
-#include "tcuTestCaseWrapper.hpp"
-#include "gluRenderContext.hpp"
-
-namespace deqp
-{
-namespace gles2
-{
-
-class TestCaseWrapper : public tcu::TestCaseWrapper
-{
-public:
-											TestCaseWrapper			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx);
-	virtual									~TestCaseWrapper		(void);
-
-	virtual bool							initTestCase			(tcu::TestCase* testCase);
-
-	// If deinit returns false, test execution will be aborted.
-	virtual bool							deinitTestCase			(tcu::TestCase* testCase);
-	virtual tcu::TestNode::IterateResult	iterateTestCase			(tcu::TestCase* testCase);
-
-private:
-	glu::RenderContext&						m_renderCtx;
-};
-
-} // gles2
-} // deqp
-
-#endif // _TES2TESTCASEWRAPPER_HPP
diff --git a/modules/gles2/tes2TestPackage.cpp b/modules/gles2/tes2TestPackage.cpp
index 7eeb087..21c0abb 100644
--- a/modules/gles2/tes2TestPackage.cpp
+++ b/modules/gles2/tes2TestPackage.cpp
@@ -28,40 +28,93 @@
 #include "tes2CapabilityTests.hpp"
 #include "es2aAccuracyTests.hpp"
 #include "es2sStressTests.hpp"
+#include "tcuTestLog.hpp"
+#include "gluRenderContext.hpp"
+#include "gluStateReset.hpp"
+#include "glwFunctions.hpp"
+#include "glwEnums.hpp"
 
 namespace deqp
 {
 namespace gles2
 {
 
-PackageContext::PackageContext (tcu::TestContext& testCtx)
-	: m_context		(DE_NULL)
-	, m_caseWrapper	(DE_NULL)
+class TestCaseWrapper : public tcu::TestCaseExecutor
 {
-	try
-	{
-		m_context		= new Context(testCtx);
-		m_caseWrapper	= new TestCaseWrapper(testCtx, m_context->getRenderContext());
-	}
-	catch (...)
-	{
-		delete m_caseWrapper;
-		delete m_context;
+public:
+									TestCaseWrapper		(TestPackage& package);
+									~TestCaseWrapper	(void);
 
-		throw;
-	}
+	void							init				(tcu::TestCase* testCase, const std::string& path);
+	void							deinit				(tcu::TestCase* testCase);
+	tcu::TestNode::IterateResult	iterate				(tcu::TestCase* testCase);
+
+private:
+	TestPackage&					m_testPackage;
+};
+
+TestCaseWrapper::TestCaseWrapper (TestPackage& package)
+	: m_testPackage(package)
+{
 }
 
-PackageContext::~PackageContext (void)
+TestCaseWrapper::~TestCaseWrapper (void)
 {
-	delete m_caseWrapper;
-	delete m_context;
+}
+
+void TestCaseWrapper::init (tcu::TestCase* testCase, const std::string&)
+{
+	testCase->init();
+}
+
+void TestCaseWrapper::deinit (tcu::TestCase* testCase)
+{
+	testCase->deinit();
+
+	DE_ASSERT(m_testPackage.getContext());
+	glu::resetState(m_testPackage.getContext()->getRenderContext());
+}
+
+tcu::TestNode::IterateResult TestCaseWrapper::iterate (tcu::TestCase* testCase)
+{
+	tcu::TestContext&				testCtx		= m_testPackage.getContext()->getTestContext();
+	glu::RenderContext&				renderCtx	= m_testPackage.getContext()->getRenderContext();
+	tcu::TestCase::IterateResult	result;
+
+	// Clear to surrender-blue
+	{
+		const glw::Functions& gl = renderCtx.getFunctions();
+		gl.clearColor(0.125f, 0.25f, 0.5f, 1.f);
+		gl.clear(GL_COLOR_BUFFER_BIT);
+	}
+
+	result = testCase->iterate();
+
+	// Call implementation specific post-iterate routine (usually handles native events and swaps buffers)
+	try
+	{
+		renderCtx.postIterate();
+		return result;
+	}
+	catch (const tcu::ResourceError& e)
+	{
+		testCtx.getLog() << e;
+		testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Resource error in context post-iteration routine");
+		testCtx.setTerminateAfter(true);
+		return tcu::TestNode::STOP;
+	}
+	catch (const std::exception& e)
+	{
+		testCtx.getLog() << e;
+		testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Error in context post-iteration routine");
+		return tcu::TestNode::STOP;
+	}
 }
 
 TestPackage::TestPackage (tcu::TestContext& testCtx)
 	: tcu::TestPackage	(testCtx, "dEQP-GLES2", "dEQP OpenGL ES 2.0 Tests")
-	, m_packageCtx		(DE_NULL)
 	, m_archive			(testCtx.getRootArchive(), "gles2/")
+	, m_context			(DE_NULL)
 {
 }
 
@@ -69,7 +122,7 @@
 {
 	// Destroy children first since destructors may access context.
 	TestNode::deinit();
-	delete m_packageCtx;
+	delete m_context;
 }
 
 void TestPackage::init (void)
@@ -77,20 +130,20 @@
 	try
 	{
 		// Create context
-		m_packageCtx = new PackageContext(m_testCtx);
+		m_context = new Context(m_testCtx);
 
 		// Add main test groups
-		addChild(new InfoTests						(m_packageCtx->getContext()));
-		addChild(new CapabilityTests				(m_packageCtx->getContext()));
-		addChild(new Functional::FunctionalTests	(m_packageCtx->getContext()));
-		addChild(new Accuracy::AccuracyTests		(m_packageCtx->getContext()));
-		addChild(new Performance::PerformanceTests	(m_packageCtx->getContext()));
-		addChild(new Stress::StressTests			(m_packageCtx->getContext()));
+		addChild(new InfoTests						(*m_context));
+		addChild(new CapabilityTests				(*m_context));
+		addChild(new Functional::FunctionalTests	(*m_context));
+		addChild(new Accuracy::AccuracyTests		(*m_context));
+		addChild(new Performance::PerformanceTests	(*m_context));
+		addChild(new Stress::StressTests			(*m_context));
 	}
 	catch (...)
 	{
-		delete m_packageCtx;
-		m_packageCtx = DE_NULL;
+		delete m_context;
+		m_context = DE_NULL;
 
 		throw;
 	}
@@ -99,8 +152,13 @@
 void TestPackage::deinit (void)
 {
 	TestNode::deinit();
-	delete m_packageCtx;
-	m_packageCtx = DE_NULL;
+	delete m_context;
+	m_context = DE_NULL;
+}
+
+tcu::TestCaseExecutor* TestPackage::createExecutor (void) const
+{
+	return new TestCaseWrapper(const_cast<TestPackage&>(*this));
 }
 
 } // gles2
diff --git a/modules/gles2/tes2TestPackage.hpp b/modules/gles2/tes2TestPackage.hpp
index 3584ba0..55eadd1 100644
--- a/modules/gles2/tes2TestPackage.hpp
+++ b/modules/gles2/tes2TestPackage.hpp
@@ -25,7 +25,6 @@
 
 #include "tcuDefs.hpp"
 #include "tcuTestPackage.hpp"
-#include "tes2TestCaseWrapper.hpp"
 #include "tes2Context.hpp"
 #include "tcuResource.hpp"
 
@@ -34,20 +33,6 @@
 namespace gles2
 {
 
-class PackageContext
-{
-public:
-									PackageContext			(tcu::TestContext& testCtx);
-									~PackageContext			(void);
-
-	Context&						getContext				(void) { return *m_context;		}
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return *m_caseWrapper; }
-
-private:
-	Context*						m_context;
-	TestCaseWrapper*				m_caseWrapper;
-};
-
 class TestPackage : public tcu::TestPackage
 {
 public:
@@ -57,12 +42,14 @@
 	virtual void					init					(void);
 	virtual void					deinit					(void);
 
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return m_packageCtx->getTestCaseWrapper(); }
-	tcu::Archive&					getArchive				(void) { return m_archive; }
+	tcu::TestCaseExecutor*			createExecutor			(void) const;
+
+	tcu::Archive*					getArchive				(void) { return &m_archive;	}
+	Context*						getContext				(void) { return m_context;	}
 
 private:
-	PackageContext*					m_packageCtx;
 	tcu::ResourcePrefix				m_archive;
+	Context*						m_context;
 };
 
 } // gles2
diff --git a/modules/gles3/CMakeLists.txt b/modules/gles3/CMakeLists.txt
index e709dff..fee4280 100644
--- a/modules/gles3/CMakeLists.txt
+++ b/modules/gles3/CMakeLists.txt
@@ -24,8 +24,6 @@
 	tes3InfoTests.hpp
 	tes3TestCase.cpp
 	tes3TestCase.hpp
-	tes3TestCaseWrapper.cpp
-	tes3TestCaseWrapper.hpp
 	tes3TestPackage.cpp
 	tes3TestPackage.hpp
 	)
diff --git a/modules/gles3/tes3TestCaseWrapper.cpp b/modules/gles3/tes3TestCaseWrapper.cpp
deleted file mode 100644
index d2e2446..0000000
--- a/modules/gles3/tes3TestCaseWrapper.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES 3.0 Module
- * -------------------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief OpenGL ES 3.0 Test Case Wrapper.
- *//*--------------------------------------------------------------------*/
-
-#include "tes3TestCaseWrapper.hpp"
-#include "gluStateReset.hpp"
-#include "tcuTestLog.hpp"
-#include "glwEnums.hpp"
-#include "glwFunctions.hpp"
-
-namespace deqp
-{
-namespace gles3
-{
-
-using tcu::TestLog;
-
-TestCaseWrapper::TestCaseWrapper (tcu::TestContext& testCtx, glu::RenderContext& renderCtx)
-	: tcu::TestCaseWrapper	(testCtx)
-	, m_renderCtx			(renderCtx)
-{
-	TCU_CHECK(contextSupports(renderCtx.getType(), glu::ApiType::es(3,0)));
-}
-
-TestCaseWrapper::~TestCaseWrapper (void)
-{
-}
-
-bool TestCaseWrapper::initTestCase (tcu::TestCase* testCase)
-{
-	return tcu::TestCaseWrapper::initTestCase(testCase);
-}
-
-bool TestCaseWrapper::deinitTestCase (tcu::TestCase* testCase)
-{
-	TestLog& log = m_testCtx.getLog();
-
-	if (!tcu::TestCaseWrapper::deinitTestCase(testCase))
-		return false;
-
-	try
-	{
-		// Reset state
-		glu::resetState(m_renderCtx);
-	}
-	catch (const std::exception& e)
-	{
-		log << e;
-		log << TestLog::Message << "Error in state reset, test program will terminate." << TestLog::EndMessage;
-		return false;
-	}
-
-	return true;
-}
-
-tcu::TestNode::IterateResult TestCaseWrapper::iterateTestCase (tcu::TestCase* testCase)
-{
-	tcu::TestCase::IterateResult result = tcu::TestNode::STOP;
-
-	// Clear to surrender-blue
-	{
-		const glw::Functions& gl = m_renderCtx.getFunctions();
-		gl.clearColor(0.125f, 0.25f, 0.5f, 1.f);
-		gl.clear(GL_COLOR_BUFFER_BIT);
-	}
-
-	result = tcu::TestCaseWrapper::iterateTestCase(testCase);
-
-	// Call implementation specific post-iterate routine (usually handles native events and swaps buffers)
-	try
-	{
-		m_renderCtx.postIterate();
-		return result;
-	}
-	catch (const tcu::ResourceError& e)
-	{
-		m_testCtx.getLog() << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Resource error in context post-iteration routine");
-		m_testCtx.setTerminateAfter(true);
-		return tcu::TestNode::STOP;
-	}
-	catch (const std::exception& e)
-	{
-		m_testCtx.getLog() << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Error in context post-iteration routine");
-		return tcu::TestNode::STOP;
-	}
-}
-
-} // gles3
-} // deqp
diff --git a/modules/gles3/tes3TestCaseWrapper.hpp b/modules/gles3/tes3TestCaseWrapper.hpp
deleted file mode 100644
index 6ac92fb..0000000
--- a/modules/gles3/tes3TestCaseWrapper.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef _TES3TESTCASEWRAPPER_HPP
-#define _TES3TESTCASEWRAPPER_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES 3.0 Module
- * -------------------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief OpenGL ES 3.0 Test Case Wrapper.
- *//*--------------------------------------------------------------------*/
-
-#include "tcuDefs.hpp"
-#include "tcuTestCaseWrapper.hpp"
-#include "gluRenderContext.hpp"
-
-namespace deqp
-{
-namespace gles3
-{
-
-class TestCaseWrapper : public tcu::TestCaseWrapper
-{
-public:
-											TestCaseWrapper			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx);
-	virtual									~TestCaseWrapper		(void);
-
-	virtual bool							initTestCase			(tcu::TestCase* testCase);
-
-	// If deinit returns false, test execution will be aborted.
-	virtual bool							deinitTestCase			(tcu::TestCase* testCase);
-	virtual tcu::TestNode::IterateResult	iterateTestCase			(tcu::TestCase* testCase);
-
-private:
-	glu::RenderContext&						m_renderCtx;
-};
-
-} // gles3
-} // deqp
-
-#endif // _TES3TESTCASEWRAPPER_HPP
diff --git a/modules/gles3/tes3TestPackage.cpp b/modules/gles3/tes3TestPackage.cpp
index 6f569f9..f0f2289 100644
--- a/modules/gles3/tes3TestPackage.cpp
+++ b/modules/gles3/tes3TestPackage.cpp
@@ -27,40 +27,93 @@
 #include "es3aAccuracyTests.hpp"
 #include "es3sStressTests.hpp"
 #include "es3pPerformanceTests.hpp"
+#include "tcuTestLog.hpp"
+#include "gluRenderContext.hpp"
+#include "gluStateReset.hpp"
+#include "glwFunctions.hpp"
+#include "glwEnums.hpp"
 
 namespace deqp
 {
 namespace gles3
 {
 
-PackageContext::PackageContext (tcu::TestContext& testCtx)
-	: m_context		(DE_NULL)
-	, m_caseWrapper	(DE_NULL)
+class TestCaseWrapper : public tcu::TestCaseExecutor
 {
-	try
-	{
-		m_context		= new Context(testCtx);
-		m_caseWrapper	= new TestCaseWrapper(testCtx, m_context->getRenderContext());
-	}
-	catch (...)
-	{
-		delete m_caseWrapper;
-		delete m_context;
+public:
+									TestCaseWrapper		(TestPackage& package);
+									~TestCaseWrapper	(void);
 
-		throw;
-	}
+	void							init				(tcu::TestCase* testCase, const std::string& path);
+	void							deinit				(tcu::TestCase* testCase);
+	tcu::TestNode::IterateResult	iterate				(tcu::TestCase* testCase);
+
+private:
+	TestPackage&					m_testPackage;
+};
+
+TestCaseWrapper::TestCaseWrapper (TestPackage& package)
+	: m_testPackage(package)
+{
 }
 
-PackageContext::~PackageContext (void)
+TestCaseWrapper::~TestCaseWrapper (void)
 {
-	delete m_caseWrapper;
-	delete m_context;
+}
+
+void TestCaseWrapper::init (tcu::TestCase* testCase, const std::string&)
+{
+	testCase->init();
+}
+
+void TestCaseWrapper::deinit (tcu::TestCase* testCase)
+{
+	testCase->deinit();
+
+	DE_ASSERT(m_testPackage.getContext());
+	glu::resetState(m_testPackage.getContext()->getRenderContext());
+}
+
+tcu::TestNode::IterateResult TestCaseWrapper::iterate (tcu::TestCase* testCase)
+{
+	tcu::TestContext&				testCtx		= m_testPackage.getContext()->getTestContext();
+	glu::RenderContext&				renderCtx	= m_testPackage.getContext()->getRenderContext();
+	tcu::TestCase::IterateResult	result;
+
+	// Clear to surrender-blue
+	{
+		const glw::Functions& gl = renderCtx.getFunctions();
+		gl.clearColor(0.125f, 0.25f, 0.5f, 1.f);
+		gl.clear(GL_COLOR_BUFFER_BIT);
+	}
+
+	result = testCase->iterate();
+
+	// Call implementation specific post-iterate routine (usually handles native events and swaps buffers)
+	try
+	{
+		renderCtx.postIterate();
+		return result;
+	}
+	catch (const tcu::ResourceError& e)
+	{
+		testCtx.getLog() << e;
+		testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Resource error in context post-iteration routine");
+		testCtx.setTerminateAfter(true);
+		return tcu::TestNode::STOP;
+	}
+	catch (const std::exception& e)
+	{
+		testCtx.getLog() << e;
+		testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Error in context post-iteration routine");
+		return tcu::TestNode::STOP;
+	}
 }
 
 TestPackage::TestPackage (tcu::TestContext& testCtx)
 	: tcu::TestPackage	(testCtx, "dEQP-GLES3", "dEQP OpenGL ES 3.0 Tests")
-	, m_packageCtx		(DE_NULL)
 	, m_archive			(testCtx.getRootArchive(), "gles3/")
+	, m_context			(DE_NULL)
 {
 }
 
@@ -68,7 +121,7 @@
 {
 	// Destroy children first since destructors may access context.
 	TestNode::deinit();
-	delete m_packageCtx;
+	delete m_context;
 }
 
 void TestPackage::init (void)
@@ -76,19 +129,19 @@
 	try
 	{
 		// Create context
-		m_packageCtx = new PackageContext(m_testCtx);
+		m_context = new Context(m_testCtx);
 
 		// Add main test groups
-		addChild(new InfoTests						(m_packageCtx->getContext()));
-		addChild(new Functional::FunctionalTests	(m_packageCtx->getContext()));
-		addChild(new Accuracy::AccuracyTests		(m_packageCtx->getContext()));
-		addChild(new Performance::PerformanceTests	(m_packageCtx->getContext()));
-		addChild(new Stress::StressTests			(m_packageCtx->getContext()));
+		addChild(new InfoTests						(*m_context));
+		addChild(new Functional::FunctionalTests	(*m_context));
+		addChild(new Accuracy::AccuracyTests		(*m_context));
+		addChild(new Performance::PerformanceTests	(*m_context));
+		addChild(new Stress::StressTests			(*m_context));
 	}
 	catch (...)
 	{
-		delete m_packageCtx;
-		m_packageCtx = DE_NULL;
+		delete m_context;
+		m_context = DE_NULL;
 
 		throw;
 	}
@@ -97,8 +150,13 @@
 void TestPackage::deinit (void)
 {
 	TestNode::deinit();
-	delete m_packageCtx;
-	m_packageCtx = DE_NULL;
+	delete m_context;
+	m_context = DE_NULL;
+}
+
+tcu::TestCaseExecutor* TestPackage::createExecutor (void) const
+{
+	return new TestCaseWrapper(const_cast<TestPackage&>(*this));
 }
 
 } // gles3
diff --git a/modules/gles3/tes3TestPackage.hpp b/modules/gles3/tes3TestPackage.hpp
index c58b15d..68c8cbf 100644
--- a/modules/gles3/tes3TestPackage.hpp
+++ b/modules/gles3/tes3TestPackage.hpp
@@ -25,7 +25,6 @@
 
 #include "tcuDefs.hpp"
 #include "tcuTestPackage.hpp"
-#include "tes3TestCaseWrapper.hpp"
 #include "tes3Context.hpp"
 #include "tcuResource.hpp"
 
@@ -34,20 +33,6 @@
 namespace gles3
 {
 
-class PackageContext
-{
-public:
-									PackageContext			(tcu::TestContext& testCtx);
-									~PackageContext			(void);
-
-	Context&						getContext				(void) { return *m_context;		}
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return *m_caseWrapper; }
-
-private:
-	Context*						m_context;
-	TestCaseWrapper*				m_caseWrapper;
-};
-
 class TestPackage : public tcu::TestPackage
 {
 public:
@@ -57,12 +42,14 @@
 	virtual void					init					(void);
 	virtual void					deinit					(void);
 
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return m_packageCtx->getTestCaseWrapper(); }
-	tcu::Archive&					getArchive				(void) { return m_archive; }
+	tcu::TestCaseExecutor*			createExecutor			(void) const;
+
+	tcu::Archive*					getArchive				(void) { return &m_archive;	}
+	Context*						getContext				(void) { return m_context;	}
 
 private:
-	PackageContext*					m_packageCtx;
 	tcu::ResourcePrefix				m_archive;
+	Context*						m_context;
 };
 
 } // gles3
diff --git a/modules/gles31/CMakeLists.txt b/modules/gles31/CMakeLists.txt
index 1bdf423..0d4cfc9 100644
--- a/modules/gles31/CMakeLists.txt
+++ b/modules/gles31/CMakeLists.txt
@@ -20,8 +20,6 @@
 	tes31InfoTests.hpp
 	tes31TestCase.cpp
 	tes31TestCase.hpp
-	tes31TestCaseWrapper.cpp
-	tes31TestCaseWrapper.hpp
 	tes31TestPackage.cpp
 	tes31TestPackage.hpp
 	)
diff --git a/modules/gles31/tes31TestCase.cpp b/modules/gles31/tes31TestCase.cpp
index 53af1d4..9c9c6c0 100644
--- a/modules/gles31/tes31TestCase.cpp
+++ b/modules/gles31/tes31TestCase.cpp
@@ -18,7 +18,9 @@
  *
  *//*!
  * \file
- * \brief OpenGL ES 3plus test case.
+ * \brief OpenGL ES 3.1 test case.
  *//*--------------------------------------------------------------------*/
 
 #include "tes31TestCase.hpp"
+
+DE_EMPTY_CPP_FILE
diff --git a/modules/gles31/tes31TestCaseWrapper.cpp b/modules/gles31/tes31TestCaseWrapper.cpp
deleted file mode 100644
index e45ab8e..0000000
--- a/modules/gles31/tes31TestCaseWrapper.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES 3.1 Module
- * -------------------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief OpenGL ES 3.1 Test Case Wrapper.
- *//*--------------------------------------------------------------------*/
-
-#include "tes31TestCaseWrapper.hpp"
-#include "gluStateReset.hpp"
-#include "tcuTestLog.hpp"
-#include "glwEnums.hpp"
-#include "glwFunctions.hpp"
-
-namespace deqp
-{
-namespace gles31
-{
-
-using tcu::TestLog;
-
-TestCaseWrapper::TestCaseWrapper (tcu::TestContext& testCtx, glu::RenderContext& renderCtx)
-	: tcu::TestCaseWrapper	(testCtx)
-	, m_renderCtx			(renderCtx)
-{
-//	TCU_CHECK(renderCtx.getType() == glu::CONTEXTTYPE_GL43_CORE);
-}
-
-TestCaseWrapper::~TestCaseWrapper (void)
-{
-}
-
-bool TestCaseWrapper::initTestCase (tcu::TestCase* testCase)
-{
-	return tcu::TestCaseWrapper::initTestCase(testCase);
-}
-
-bool TestCaseWrapper::deinitTestCase (tcu::TestCase* testCase)
-{
-	TestLog& log = m_testCtx.getLog();
-
-	if (!tcu::TestCaseWrapper::deinitTestCase(testCase))
-		return false;
-
-	try
-	{
-		// Reset state
-		glu::resetState(m_renderCtx);
-	}
-	catch (const std::exception& e)
-	{
-		log << e;
-		log << TestLog::Message << "Error in state reset, test program will terminate." << TestLog::EndMessage;
-		return false;
-	}
-
-	return true;
-}
-
-tcu::TestNode::IterateResult TestCaseWrapper::iterateTestCase (tcu::TestCase* testCase)
-{
-	tcu::TestCase::IterateResult result = tcu::TestCaseWrapper::iterateTestCase(testCase);
-
-	// Call implementation specific post-iterate routine (usually handles native events and swaps buffers)
-	try
-	{
-		m_renderCtx.postIterate();
-		return result;
-	}
-	catch (const tcu::ResourceError& e)
-	{
-		m_testCtx.getLog() << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Resource error in context post-iteration routine");
-		m_testCtx.setTerminateAfter(true);
-		return tcu::TestNode::STOP;
-	}
-	catch (const std::exception& e)
-	{
-		m_testCtx.getLog() << e;
-		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Error in context post-iteration routine");
-		return tcu::TestNode::STOP;
-	}
-}
-
-} // gles31
-} // deqp
diff --git a/modules/gles31/tes31TestCaseWrapper.hpp b/modules/gles31/tes31TestCaseWrapper.hpp
deleted file mode 100644
index 941e7f7..0000000
--- a/modules/gles31/tes31TestCaseWrapper.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef _TES31TESTCASEWRAPPER_HPP
-#define _TES31TESTCASEWRAPPER_HPP
-/*-------------------------------------------------------------------------
- * drawElements Quality Program OpenGL ES 3.1 Module
- * -------------------------------------------------
- *
- * Copyright 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *//*!
- * \file
- * \brief OpenGL ES 3.1 Test Case Wrapper.
- *//*--------------------------------------------------------------------*/
-
-#include "tcuDefs.hpp"
-#include "tcuTestCaseWrapper.hpp"
-#include "gluRenderContext.hpp"
-
-namespace deqp
-{
-namespace gles31
-{
-
-class TestCaseWrapper : public tcu::TestCaseWrapper
-{
-public:
-											TestCaseWrapper			(tcu::TestContext& testCtx, glu::RenderContext& renderCtx);
-	virtual									~TestCaseWrapper		(void);
-
-	virtual bool							initTestCase			(tcu::TestCase* testCase);
-
-	// If deinit returns false, test execution will be aborted.
-	virtual bool							deinitTestCase			(tcu::TestCase* testCase);
-	virtual tcu::TestNode::IterateResult	iterateTestCase			(tcu::TestCase* testCase);
-
-private:
-	glu::RenderContext&						m_renderCtx;
-};
-
-} // gles31
-} // deqp
-
-#endif // _TES31TESTCASEWRAPPER_HPP
diff --git a/modules/gles31/tes31TestPackage.cpp b/modules/gles31/tes31TestPackage.cpp
index 54ce5aa..51a1be9 100644
--- a/modules/gles31/tes31TestPackage.cpp
+++ b/modules/gles31/tes31TestPackage.cpp
@@ -25,40 +25,81 @@
 #include "tes31InfoTests.hpp"
 #include "es31fFunctionalTests.hpp"
 #include "es31sStressTests.hpp"
+#include "gluStateReset.hpp"
+#include "gluRenderContext.hpp"
+#include "tcuTestLog.hpp"
 
 namespace deqp
 {
 namespace gles31
 {
 
-PackageContext::PackageContext (tcu::TestContext& testCtx)
-	: m_context		(DE_NULL)
-	, m_caseWrapper	(DE_NULL)
+class TestCaseWrapper : public tcu::TestCaseExecutor
 {
-	try
-	{
-		m_context		= new Context(testCtx);
-		m_caseWrapper	= new TestCaseWrapper(testCtx, m_context->getRenderContext());
-	}
-	catch (...)
-	{
-		delete m_caseWrapper;
-		delete m_context;
+public:
+									TestCaseWrapper		(TestPackage& package);
+									~TestCaseWrapper	(void);
 
-		throw;
-	}
+	void							init				(tcu::TestCase* testCase, const std::string& path);
+	void							deinit				(tcu::TestCase* testCase);
+	tcu::TestNode::IterateResult	iterate				(tcu::TestCase* testCase);
+
+private:
+	TestPackage&					m_testPackage;
+};
+
+TestCaseWrapper::TestCaseWrapper (TestPackage& package)
+	: m_testPackage(package)
+{
 }
 
-PackageContext::~PackageContext (void)
+TestCaseWrapper::~TestCaseWrapper (void)
 {
-	delete m_caseWrapper;
-	delete m_context;
+}
+
+void TestCaseWrapper::init (tcu::TestCase* testCase, const std::string&)
+{
+	testCase->init();
+}
+
+void TestCaseWrapper::deinit (tcu::TestCase* testCase)
+{
+	testCase->deinit();
+
+	DE_ASSERT(m_testPackage.getContext());
+	glu::resetState(m_testPackage.getContext()->getRenderContext());
+}
+
+tcu::TestNode::IterateResult TestCaseWrapper::iterate (tcu::TestCase* testCase)
+{
+	tcu::TestContext&					testCtx	= m_testPackage.getContext()->getTestContext();
+	const tcu::TestCase::IterateResult	result	= testCase->iterate();
+
+	// Call implementation specific post-iterate routine (usually handles native events and swaps buffers)
+	try
+	{
+		m_testPackage.getContext()->getRenderContext().postIterate();
+		return result;
+	}
+	catch (const tcu::ResourceError& e)
+	{
+		testCtx.getLog() << e;
+		testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Resource error in context post-iteration routine");
+		testCtx.setTerminateAfter(true);
+		return tcu::TestNode::STOP;
+	}
+	catch (const std::exception& e)
+	{
+		testCtx.getLog() << e;
+		testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Error in context post-iteration routine");
+		return tcu::TestNode::STOP;
+	}
 }
 
 TestPackage::TestPackage (tcu::TestContext& testCtx)
 	: tcu::TestPackage	(testCtx, "dEQP-GLES31", "dEQP OpenGL ES 3.1 Tests")
-	, m_packageCtx		(DE_NULL)
 	, m_archive			(testCtx.getRootArchive(), "gles31/")
+	, m_context			(DE_NULL)
 {
 }
 
@@ -66,7 +107,7 @@
 {
 	// Destroy children first since destructors may access context.
 	TestNode::deinit();
-	delete m_packageCtx;
+	delete m_context;
 }
 
 void TestPackage::init (void)
@@ -74,17 +115,17 @@
 	try
 	{
 		// Create context
-		m_packageCtx = new PackageContext(m_testCtx);
+		m_context = new Context(m_testCtx);
 
 		// Add main test groups
-		addChild(new InfoTests						(m_packageCtx->getContext()));
-		addChild(new Functional::FunctionalTests	(m_packageCtx->getContext()));
-		addChild(new Stress::StressTests			(m_packageCtx->getContext()));
+		addChild(new InfoTests						(*m_context));
+		addChild(new Functional::FunctionalTests	(*m_context));
+		addChild(new Stress::StressTests			(*m_context));
 	}
 	catch (...)
 	{
-		delete m_packageCtx;
-		m_packageCtx = DE_NULL;
+		delete m_context;
+		m_context = DE_NULL;
 
 		throw;
 	}
@@ -93,8 +134,13 @@
 void TestPackage::deinit (void)
 {
 	TestNode::deinit();
-	delete m_packageCtx;
-	m_packageCtx = DE_NULL;
+	delete m_context;
+	m_context = DE_NULL;
+}
+
+tcu::TestCaseExecutor* TestPackage::createExecutor (void) const
+{
+	return new TestCaseWrapper(const_cast<TestPackage&>(*this));
 }
 
 } // gles31
diff --git a/modules/gles31/tes31TestPackage.hpp b/modules/gles31/tes31TestPackage.hpp
index cfe339b..1372f83 100644
--- a/modules/gles31/tes31TestPackage.hpp
+++ b/modules/gles31/tes31TestPackage.hpp
@@ -25,7 +25,6 @@
 
 #include "tcuDefs.hpp"
 #include "tcuTestPackage.hpp"
-#include "tes31TestCaseWrapper.hpp"
 #include "tes31Context.hpp"
 #include "tcuResource.hpp"
 
@@ -34,20 +33,6 @@
 namespace gles31
 {
 
-class PackageContext
-{
-public:
-									PackageContext			(tcu::TestContext& testCtx);
-									~PackageContext			(void);
-
-	Context&						getContext				(void) { return *m_context;		}
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return *m_caseWrapper; }
-
-private:
-	Context*						m_context;
-	TestCaseWrapper*				m_caseWrapper;
-};
-
 class TestPackage : public tcu::TestPackage
 {
 public:
@@ -57,12 +42,14 @@
 	virtual void					init					(void);
 	virtual void					deinit					(void);
 
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return m_packageCtx->getTestCaseWrapper(); }
-	tcu::Archive&					getArchive				(void) { return m_archive; }
+	tcu::TestCaseExecutor*			createExecutor			(void) const;
+
+	tcu::Archive*					getArchive				(void) { return &m_archive; }
+	Context*						getContext				(void) { return m_context; }
 
 private:
-	PackageContext*					m_packageCtx;
 	tcu::ResourcePrefix				m_archive;
+	Context*						m_context;
 };
 
 } // gles31
diff --git a/modules/internal/ditImageCompareTests.cpp b/modules/internal/ditImageCompareTests.cpp
index 96e0a5f..899b488 100644
--- a/modules/internal/ditImageCompareTests.cpp
+++ b/modules/internal/ditImageCompareTests.cpp
@@ -38,7 +38,7 @@
 
 using tcu::TestLog;
 
-static const char*	BASE_DIR			= "data/imagecompare";
+static const char*	BASE_DIR			= "internal/data/imagecompare";
 
 static void loadImageRGBA8 (tcu::TextureLevel& dst, const tcu::Archive& archive, const char* path)
 {
diff --git a/modules/internal/ditImageIOTests.cpp b/modules/internal/ditImageIOTests.cpp
index fde67f3..c9a7178 100644
--- a/modules/internal/ditImageIOTests.cpp
+++ b/modules/internal/ditImageIOTests.cpp
@@ -90,10 +90,10 @@
 
 	void init (void)
 	{
-		addChild(new ImageReadCase(m_testCtx, "rgb24_256x256",	"data/imageio/rgb24_256x256.png",	0x6efad777));
-		addChild(new ImageReadCase(m_testCtx, "rgb24_209x181",	"data/imageio/rgb24_209x181.png",	0xfd6ea668));
-		addChild(new ImageReadCase(m_testCtx, "rgba32_256x256",	"data/imageio/rgba32_256x256.png",	0xcf4883da));
-		addChild(new ImageReadCase(m_testCtx, "rgba32_207x219",	"data/imageio/rgba32_207x219.png",	0x404ba06b));
+		addChild(new ImageReadCase(m_testCtx, "rgb24_256x256",	"internal/data/imageio/rgb24_256x256.png",	0x6efad777));
+		addChild(new ImageReadCase(m_testCtx, "rgb24_209x181",	"internal/data/imageio/rgb24_209x181.png",	0xfd6ea668));
+		addChild(new ImageReadCase(m_testCtx, "rgba32_256x256",	"internal/data/imageio/rgba32_256x256.png",	0xcf4883da));
+		addChild(new ImageReadCase(m_testCtx, "rgba32_207x219",	"internal/data/imageio/rgba32_207x219.png",	0x404ba06b));
 	}
 };
 
diff --git a/modules/internal/ditTestPackage.cpp b/modules/internal/ditTestPackage.cpp
index 22c75b2..e2be5cd 100644
--- a/modules/internal/ditTestPackage.cpp
+++ b/modules/internal/ditTestPackage.cpp
@@ -50,9 +50,35 @@
 	}
 };
 
+class TestCaseExecutor : public tcu::TestCaseExecutor
+{
+public:
+	TestCaseExecutor (void)
+	{
+	}
+
+	~TestCaseExecutor (void)
+	{
+	}
+
+	void init (tcu::TestCase* testCase, const std::string&)
+	{
+		testCase->init();
+	}
+
+	void deinit (tcu::TestCase* testCase)
+	{
+		testCase->deinit();
+	}
+
+	tcu::TestNode::IterateResult iterate (tcu::TestCase* testCase)
+	{
+		return testCase->iterate();
+	}
+};
+
 TestPackage::TestPackage (tcu::TestContext& testCtx)
 	: tcu::TestPackage	(testCtx, "dE-IT", "drawElements Internal Tests")
-	, m_wrapper			(testCtx)
 	, m_archive			(testCtx.getRootArchive(), "internal/")
 {
 }
@@ -69,9 +95,9 @@
 	addChild(new DeqpTests		(m_testCtx));
 }
 
-void TestPackage::deinit (void)
+tcu::TestCaseExecutor* TestPackage::createExecutor (void) const
 {
-	TestNode::deinit();
+	return new TestCaseExecutor();
 }
 
 } // dit
diff --git a/modules/internal/ditTestPackage.hpp b/modules/internal/ditTestPackage.hpp
index 0d08a06..c42a9ee 100644
--- a/modules/internal/ditTestPackage.hpp
+++ b/modules/internal/ditTestPackage.hpp
@@ -37,13 +37,10 @@
 	virtual							~TestPackage			(void);
 
 	virtual void					init					(void);
-	virtual void					deinit					(void);
-
-	tcu::TestCaseWrapper&			getTestCaseWrapper		(void) { return m_wrapper; }
-	tcu::Archive&					getArchive				(void) { return m_archive; }
+	tcu::TestCaseExecutor*			createExecutor			(void) const;
+	tcu::Archive*					getArchive				(void) { return &m_archive; }
 
 private:
-	tcu::TestCaseWrapper			m_wrapper;
 	tcu::ResourcePrefix				m_archive;
 };