Home | History | Annotate | Download | only in common
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Tester Core
      3  * ----------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Class for executing tests.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "tcuTestExecutor.hpp"
     25 #include "tcuCommandLine.hpp"
     26 #include "tcuPlatform.hpp"
     27 #include "tcuTestLog.hpp"
     28 
     29 #include "deInt32.h"
     30 
     31 #include <typeinfo>
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 
     37 using std::string;
     38 using std::vector;
     39 
     40 namespace tcu
     41 {
     42 
     43 TestExecutor::TestExecutor (TestContext& testCtx, const CommandLine& cmdLine)
     44 	: m_testCtx				(testCtx)
     45 	, m_cmdLine				(cmdLine)
     46 	, m_rootNode			(DE_NULL)
     47 	, m_testCaseWrapper		(DE_NULL)
     48 	, m_testCaseListFile	(DE_NULL)
     49 	, m_testCaseListWriter	(DE_NULL)
     50 {
     51 	m_abortSession	= false;
     52 	m_isInTestCase	= false;
     53 
     54 	// Create the root node.
     55 	TestPackageRegistry*						packageRegistry	= TestPackageRegistry::getSingleton();
     56 	vector<TestPackageRegistry::PackageInfo*>	packageInfos	= packageRegistry->getPackageInfos();
     57 	vector<TestNode*>							testPackages;
     58 
     59 	for (int i = 0; i < (int)packageInfos.size(); i++)
     60 		testPackages.push_back(packageInfos[i]->createFunc(testCtx));
     61 
     62 	m_rootNode = new TestPackageRoot(testCtx, testPackages);
     63 
     64 	// Init traverse stack.
     65 	NodeIter iter(m_rootNode);
     66 	m_sessionStack.push_back(iter);
     67 }
     68 
     69 TestExecutor::~TestExecutor (void)
     70 {
     71 	if (m_testCaseListWriter)
     72 		qpXmlWriter_destroy(m_testCaseListWriter);
     73 
     74 	if (m_testCaseListFile)
     75 		fclose(m_testCaseListFile);
     76 
     77 	delete m_rootNode;
     78 }
     79 
     80 // Test sub-case iteration.
     81 void TestExecutor::enterTestPackage (TestPackage* testPackage, const char* packageName)
     82 {
     83 	DE_ASSERT(testPackage && packageName);
     84 
     85 	// Open file/writer for case dumping.
     86 	const RunMode runMode = m_cmdLine.getRunMode();
     87 	if (runMode == RUNMODE_DUMP_XML_CASELIST || runMode == RUNMODE_DUMP_TEXT_CASELIST)
     88 	{
     89 		const char* const	ext				= (runMode == RUNMODE_DUMP_XML_CASELIST) ? "xml" : "txt";
     90 		const string		fileName		= string(packageName) + "-cases." + ext;
     91 
     92 		print("Dumping all test case names in '%s' to file '%s'..\n", packageName, fileName.c_str());
     93 		TCU_CHECK(m_testCaseListFile = fopen(fileName.c_str(), "wb"));
     94 
     95 		if (runMode == RUNMODE_DUMP_XML_CASELIST)
     96 		{
     97 			TCU_CHECK(m_testCaseListWriter = qpXmlWriter_createFileWriter(m_testCaseListFile, DE_FALSE));
     98 
     99 			qpXmlWriter_startDocument(m_testCaseListWriter);
    100 			qpXmlWriter_startElement(m_testCaseListWriter, "TestCaseList", 0, DE_NULL);
    101 		}
    102 	}
    103 
    104 	// Initialize package.
    105 	testPackage->init();
    106 
    107 	// Store test case wrapper
    108 	m_testCaseWrapper = &testPackage->getTestCaseWrapper();
    109 	DE_ASSERT(m_testCaseWrapper);
    110 
    111 	// Set archive.
    112 	m_testCtx.setCurrentArchive(testPackage->getArchive());
    113 }
    114 
    115 void TestExecutor::leaveTestPackage (TestPackage* testPackage)
    116 {
    117 	DE_ASSERT(testPackage);
    118 
    119 	const RunMode runMode = m_cmdLine.getRunMode();
    120 	if (runMode == RUNMODE_DUMP_XML_CASELIST)
    121 	{
    122 		qpXmlWriter_endElement(m_testCaseListWriter, "TestCaseList");
    123 		qpXmlWriter_endDocument(m_testCaseListWriter);
    124 		qpXmlWriter_destroy(m_testCaseListWriter);
    125 		m_testCaseListWriter = DE_NULL;
    126 	}
    127 
    128 	if (runMode == RUNMODE_DUMP_TEXT_CASELIST || runMode == RUNMODE_DUMP_XML_CASELIST)
    129 	{
    130 		fclose(m_testCaseListFile);
    131 		m_testCaseListFile = DE_NULL;
    132 	}
    133 
    134 	DE_ASSERT(!m_testCaseListWriter && !m_testCaseListFile);
    135 
    136 	m_testCaseWrapper = DE_NULL;
    137 	m_testCtx.setCurrentArchive(m_testCtx.getRootArchive());
    138 
    139 	// Deinitialize package.
    140 	testPackage->deinit();
    141 }
    142 
    143 void TestExecutor::enterGroupNode (TestCaseGroup* testGroup, const char* casePath)
    144 {
    145 	DE_UNREF(casePath);
    146 	testGroup->init();
    147 }
    148 
    149 void TestExecutor::leaveGroupNode (TestCaseGroup* testGroup)
    150 {
    151 	testGroup->deinit();
    152 }
    153 
    154 static qpTestCaseType nodeTypeToTestCaseType (TestNodeType nodeType)
    155 {
    156 	switch (nodeType)
    157 	{
    158 		case NODETYPE_SELF_VALIDATE:	return QP_TEST_CASE_TYPE_SELF_VALIDATE;
    159 		case NODETYPE_PERFORMANCE:		return QP_TEST_CASE_TYPE_PERFORMANCE;
    160 		case NODETYPE_CAPABILITY:		return QP_TEST_CASE_TYPE_CAPABILITY;
    161 		case NODETYPE_ACCURACY:			return QP_TEST_CASE_TYPE_ACCURACY;
    162 		default:
    163 			DE_ASSERT(DE_FALSE);
    164 			return QP_TEST_CASE_TYPE_LAST;
    165 	}
    166 }
    167 
    168 bool TestExecutor::enterTestCase (TestCase* testCase, const char* casePath)
    169 {
    170 	const RunMode			runMode		= m_cmdLine.getRunMode();
    171 	const qpTestCaseType	caseType	= nodeTypeToTestCaseType(testCase->getNodeType());
    172 
    173 	if (runMode == RUNMODE_EXECUTE)
    174 	{
    175 		print("\nTest case '%s'..\n", casePath);
    176 
    177 		m_testCtx.getLog().startCase(casePath, caseType);
    178 		m_isInTestCase = true;
    179 		m_testCtx.setTestResult(QP_TEST_RESULT_LAST, "");
    180 
    181 		if (!m_testCaseWrapper->initTestCase(testCase))
    182 		{
    183 			if (m_testCtx.getTestResult() == QP_TEST_RESULT_LAST)
    184 				m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Unexpected error in subcase init");
    185 			return false;
    186 		}
    187 	}
    188 
    189 	return true;
    190 }
    191 
    192 void TestExecutor::leaveTestCase (TestCase* testCase)
    193 {
    194 	const RunMode runMode = m_cmdLine.getRunMode();
    195 	if (runMode == RUNMODE_EXECUTE)
    196 	{
    197 		// De-init case.
    198 		const bool			deinitOk		= m_testCaseWrapper->deinitTestCase(testCase);
    199 		const qpTestResult	testResult		= m_testCtx.getTestResult();
    200 		const char* const	testResultDesc	= m_testCtx.getTestResultDesc();
    201 		const bool			terminateAfter	= m_testCtx.getTerminateAfter();
    202 		DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
    203 
    204 		m_isInTestCase = false;
    205 		m_testCtx.getLog().endCase(testResult, testResultDesc);
    206 
    207 		// Update statistics.
    208 		print("  %s (%s)\n", qpGetTestResultName(testResult), testResultDesc);
    209 
    210 		m_result.numExecuted += 1;
    211 		switch (testResult)
    212 		{
    213 			case QP_TEST_RESULT_PASS:					m_result.numPassed			+= 1;	break;
    214 			case QP_TEST_RESULT_NOT_SUPPORTED:			m_result.numNotSupported	+= 1;	break;
    215 			case QP_TEST_RESULT_QUALITY_WARNING:		m_result.numWarnings		+= 1;	break;
    216 			case QP_TEST_RESULT_COMPATIBILITY_WARNING:	m_result.numWarnings		+= 1;	break;
    217 			default:									m_result.numFailed			+= 1;	break;
    218 		}
    219 
    220 		// terminateAfter, Resource error or any error in deinit means that execution should end
    221 		if (terminateAfter || !deinitOk || testResult == QP_TEST_RESULT_RESOURCE_ERROR)
    222 			m_abortSession = true;
    223 
    224 		// \todo [2011-02-09 pyry] Disable watchdog temporarily?
    225 		if (m_testCtx.getWatchDog())
    226 			qpWatchDog_reset(m_testCtx.getWatchDog());
    227 	}
    228 }
    229 
    230 // Return true while session should still continue, false otherwise.
    231 bool TestExecutor::iterate (void)
    232 {
    233 	try
    234 	{
    235 		while (!m_sessionStack.empty())
    236 		{
    237 			// Get full path to node.
    238 			string nodePath = "";
    239 			for (int ndx = 0; ndx < (int)m_sessionStack.size(); ndx++)
    240 			{
    241 				NodeIter& iter = m_sessionStack[ndx];
    242 				if (ndx > 1) // ignore root package
    243 					nodePath += ".";
    244 				nodePath += iter.node->getName();
    245 			}
    246 
    247 			// Handle the node.
    248 			NodeIter& iter = m_sessionStack[m_sessionStack.size()-1];
    249 			DE_ASSERT(iter.node != DE_NULL);
    250 			TestNode*		node	= iter.node;
    251 			bool			isLeaf	= isTestNodeTypeExecutable(node->getNodeType());
    252 
    253 			switch (iter.getState())
    254 			{
    255 				case NodeIter::STATE_BEGIN:
    256 				{
    257 					// Return to parent if name doesn't match filter.
    258 					if (!(isLeaf ? m_cmdLine.checkTestCaseName(nodePath.c_str()) : m_cmdLine.checkTestGroupName(nodePath.c_str())))
    259 					{
    260 						m_sessionStack.pop_back();
    261 						break;
    262 					}
    263 
    264 					// Enter node.
    265 					bool enterOk = true;
    266 					switch (node->getNodeType())
    267 					{
    268 						case NODETYPE_ROOT:				/* nada */																	break;
    269 						case NODETYPE_PACKAGE:			enterTestPackage(static_cast<TestPackage*>(node), nodePath.c_str());		break;
    270 						case NODETYPE_GROUP:			enterGroupNode(static_cast<TestCaseGroup*>(node), nodePath.c_str());		break;
    271 						case NODETYPE_PERFORMANCE:
    272 						case NODETYPE_CAPABILITY:
    273 						case NODETYPE_ACCURACY:			/* fall-trough */
    274 						case NODETYPE_SELF_VALIDATE:	enterOk = enterTestCase(static_cast<TestCase*>(node), nodePath.c_str());	break;
    275 						default: DE_ASSERT(false);
    276 					}
    277 
    278 					if (m_cmdLine.getRunMode() == RUNMODE_EXECUTE)
    279 					{
    280 						if (isLeaf)
    281 						{
    282 							if (enterOk)
    283 								iter.setState(NodeIter::STATE_EXECUTE_TEST);
    284 							else
    285 								iter.setState(NodeIter::STATE_FINISH);
    286 						}
    287 						else
    288 						{
    289 							iter.setState(NodeIter::STATE_TRAVERSE_CHILDREN);
    290 						}
    291 					}
    292 					else if (m_cmdLine.getRunMode() == RUNMODE_DUMP_XML_CASELIST)
    293 					{
    294 						if (node->getNodeType() != NODETYPE_ROOT && node->getNodeType() != NODETYPE_PACKAGE)
    295 						{
    296 							string			caseName	= iter.node->getName();
    297 							string			description	= iter.node->getDescription();
    298 							qpXmlAttribute	attribs[8];
    299 							int				numAttribs = 0;
    300 							const char*		caseType	= DE_NULL;
    301 
    302 							switch (node->getNodeType())
    303 							{
    304 								case NODETYPE_SELF_VALIDATE:	caseType = "SelfValidate";	break;
    305 								case NODETYPE_CAPABILITY:		caseType = "Capability";	break;
    306 								case NODETYPE_ACCURACY:			caseType = "Accuracy";		break;
    307 								case NODETYPE_PERFORMANCE:		caseType = "Performance";	break;
    308 								default:						caseType = "TestGroup";		break;
    309 							}
    310 
    311 							attribs[numAttribs++] = qpSetStringAttrib("Name", caseName.c_str());
    312 							attribs[numAttribs++] = qpSetStringAttrib("CaseType", caseType);
    313 							attribs[numAttribs++] = qpSetStringAttrib("Description", description.c_str());
    314 							qpXmlWriter_startElement(m_testCaseListWriter, "TestCase", numAttribs, attribs);
    315 						}
    316 
    317 						iter.setState(isLeaf ? NodeIter::STATE_FINISH : NodeIter::STATE_TRAVERSE_CHILDREN);
    318 					}
    319 					else if (m_cmdLine.getRunMode() == RUNMODE_DUMP_TEXT_CASELIST)
    320 					{
    321 						// \note Case list file is not open until we are in test package.
    322 						if (isLeaf)
    323 							fprintf(m_testCaseListFile, "TEST: %s\n", nodePath.c_str());
    324 						else if (node->getNodeType() != NODETYPE_ROOT)
    325 							fprintf(m_testCaseListFile, "GROUP: %s\n", nodePath.c_str());
    326 						iter.setState(isLeaf ? NodeIter::STATE_FINISH : NodeIter::STATE_TRAVERSE_CHILDREN);
    327 					}
    328 
    329 					break;
    330 				}
    331 
    332 				case NodeIter::STATE_EXECUTE_TEST:
    333 				{
    334 					// Touch the watchdog.
    335 					m_testCtx.touchWatchdog();
    336 
    337 					// Iterate the sub-case.
    338 					TestCase::IterateResult iterateResult = m_testCaseWrapper->iterateTestCase(static_cast<TestCase*>(node));
    339 
    340 					if (iterateResult == TestCase::STOP)
    341 						iter.setState(NodeIter::STATE_FINISH);
    342 
    343 					return true; // return after each iteration (when another iteration follows).
    344 				}
    345 
    346 				case NodeIter::STATE_TRAVERSE_CHILDREN:
    347 				{
    348 					int numChildren = (int)iter.children.size();
    349 					if (++iter.curChildNdx < numChildren)
    350 					{
    351 						// Push child to stack.
    352 						TestNode* childNode = iter.children[iter.curChildNdx];
    353 						m_sessionStack.push_back(NodeIter(childNode));
    354 					}
    355 					else
    356 						iter.setState(NodeIter::STATE_FINISH);
    357 
    358 					break;
    359 				}
    360 
    361 				case NodeIter::STATE_FINISH:
    362 				{
    363 					if (m_cmdLine.getRunMode() == RUNMODE_DUMP_XML_CASELIST)
    364 					{
    365 						if (node->getNodeType() != NODETYPE_ROOT && node->getNodeType() != NODETYPE_PACKAGE)
    366 							qpXmlWriter_endElement(m_testCaseListWriter, "TestCase");
    367 					}
    368 
    369 					// Leave node.
    370 					switch (node->getNodeType())
    371 					{
    372 						case NODETYPE_ROOT:				/* nada */											break;
    373 						case NODETYPE_PACKAGE:			leaveTestPackage(static_cast<TestPackage*>(node));	break;
    374 						case NODETYPE_GROUP:			leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
    375 						case NODETYPE_ACCURACY:
    376 						case NODETYPE_CAPABILITY:
    377 						case NODETYPE_PERFORMANCE:		/* fall-thru */
    378 						case NODETYPE_SELF_VALIDATE:	leaveTestCase(static_cast<TestCase*>(node));		break;
    379 						default: DE_ASSERT(false);
    380 					}
    381 
    382 					m_sessionStack.pop_back();
    383 
    384 					// Return if execution should abort.
    385 					if (m_abortSession)
    386 						return false;
    387 
    388 					// Otherwise continue iterating.
    389 					break;
    390 				}
    391 
    392 				default:
    393 					DE_ASSERT(DE_FALSE);
    394 					break;
    395 			}
    396 		}
    397 	}
    398 	catch (const std::exception& e)
    399 	{
    400 		print("TestExecutor::iterateSession(): Caught unhandled %s: %s\n", typeid(e).name(), e.what());
    401 		throw;
    402 	}
    403 
    404 	m_result.isComplete = true;
    405 	return false;
    406 }
    407 
    408 } // tcu
    409