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 		// Update statistics.
    198 		qpTestResult testResult = m_testCtx.getTestResult();
    199 		DE_ASSERT(testResult != QP_TEST_RESULT_LAST);
    200 
    201 		print("  %s (%s)\n", qpGetTestResultName(testResult), m_testCtx.getTestResultDesc());
    202 
    203 		m_result.numExecuted += 1;
    204 		switch (testResult)
    205 		{
    206 			case QP_TEST_RESULT_PASS:					m_result.numPassed			+= 1;	break;
    207 			case QP_TEST_RESULT_NOT_SUPPORTED:			m_result.numNotSupported	+= 1;	break;
    208 			case QP_TEST_RESULT_QUALITY_WARNING:		m_result.numWarnings		+= 1;	break;
    209 			case QP_TEST_RESULT_COMPATIBILITY_WARNING:	m_result.numWarnings		+= 1;	break;
    210 			default:									m_result.numFailed			+= 1;	break;
    211 		}
    212 
    213 		// De-init case.
    214 		bool deinitOk = m_testCaseWrapper->deinitTestCase(testCase);
    215 
    216 		m_isInTestCase = false;
    217 		m_testCtx.getLog().endCase(m_testCtx.getTestResult(), m_testCtx.getTestResultDesc());
    218 
    219 		// Resource error or any error in deinit means that execution should end
    220 		if (!deinitOk || testResult == QP_TEST_RESULT_RESOURCE_ERROR)
    221 			m_abortSession = true;
    222 
    223 		// \todo [2011-02-09 pyry] Disable watchdog temporarily?
    224 		if (m_testCtx.getWatchDog())
    225 			qpWatchDog_reset(m_testCtx.getWatchDog());
    226 	}
    227 }
    228 
    229 // Return true while session should still continue, false otherwise.
    230 bool TestExecutor::iterate (void)
    231 {
    232 	try
    233 	{
    234 		while (!m_sessionStack.empty())
    235 		{
    236 			// Get full path to node.
    237 			string nodePath = "";
    238 			for (int ndx = 0; ndx < (int)m_sessionStack.size(); ndx++)
    239 			{
    240 				NodeIter& iter = m_sessionStack[ndx];
    241 				if (ndx > 1) // ignore root package
    242 					nodePath += ".";
    243 				nodePath += iter.node->getName();
    244 			}
    245 
    246 			// Handle the node.
    247 			NodeIter& iter = m_sessionStack[m_sessionStack.size()-1];
    248 			DE_ASSERT(iter.node != DE_NULL);
    249 			TestNode*		node	= iter.node;
    250 			bool			isLeaf	= isTestNodeTypeExecutable(node->getNodeType());
    251 
    252 			switch (iter.getState())
    253 			{
    254 				case NodeIter::STATE_BEGIN:
    255 				{
    256 					// Return to parent if name doesn't match filter.
    257 					if (!(isLeaf ? m_cmdLine.checkTestCaseName(nodePath.c_str()) : m_cmdLine.checkTestGroupName(nodePath.c_str())))
    258 					{
    259 						m_sessionStack.pop_back();
    260 						break;
    261 					}
    262 
    263 					// Enter node.
    264 					bool enterOk = true;
    265 					switch (node->getNodeType())
    266 					{
    267 						case NODETYPE_ROOT:				/* nada */																	break;
    268 						case NODETYPE_PACKAGE:			enterTestPackage(static_cast<TestPackage*>(node), nodePath.c_str());		break;
    269 						case NODETYPE_GROUP:			enterGroupNode(static_cast<TestCaseGroup*>(node), nodePath.c_str());		break;
    270 						case NODETYPE_PERFORMANCE:
    271 						case NODETYPE_CAPABILITY:
    272 						case NODETYPE_ACCURACY:			/* fall-trough */
    273 						case NODETYPE_SELF_VALIDATE:	enterOk = enterTestCase(static_cast<TestCase*>(node), nodePath.c_str());	break;
    274 						default: DE_ASSERT(false);
    275 					}
    276 
    277 					if (m_cmdLine.getRunMode() == RUNMODE_EXECUTE)
    278 					{
    279 						if (isLeaf)
    280 						{
    281 							if (enterOk)
    282 								iter.setState(NodeIter::STATE_EXECUTE_TEST);
    283 							else
    284 								iter.setState(NodeIter::STATE_FINISH);
    285 						}
    286 						else
    287 						{
    288 							iter.setState(NodeIter::STATE_TRAVERSE_CHILDREN);
    289 						}
    290 					}
    291 					else if (m_cmdLine.getRunMode() == RUNMODE_DUMP_XML_CASELIST)
    292 					{
    293 						if (node->getNodeType() != NODETYPE_ROOT && node->getNodeType() != NODETYPE_PACKAGE)
    294 						{
    295 							string			caseName	= iter.node->getName();
    296 							string			description	= iter.node->getDescription();
    297 							qpXmlAttribute	attribs[8];
    298 							int				numAttribs = 0;
    299 							const char*		caseType	= DE_NULL;
    300 
    301 							switch (node->getNodeType())
    302 							{
    303 								case NODETYPE_SELF_VALIDATE:	caseType = "SelfValidate";	break;
    304 								case NODETYPE_CAPABILITY:		caseType = "Capability";	break;
    305 								case NODETYPE_ACCURACY:			caseType = "Accuracy";		break;
    306 								case NODETYPE_PERFORMANCE:		caseType = "Performance";	break;
    307 								default:						caseType = "TestGroup";		break;
    308 							}
    309 
    310 							attribs[numAttribs++] = qpSetStringAttrib("Name", caseName.c_str());
    311 							attribs[numAttribs++] = qpSetStringAttrib("CaseType", caseType);
    312 							attribs[numAttribs++] = qpSetStringAttrib("Description", description.c_str());
    313 							qpXmlWriter_startElement(m_testCaseListWriter, "TestCase", numAttribs, attribs);
    314 						}
    315 
    316 						iter.setState(isLeaf ? NodeIter::STATE_FINISH : NodeIter::STATE_TRAVERSE_CHILDREN);
    317 					}
    318 					else if (m_cmdLine.getRunMode() == RUNMODE_DUMP_TEXT_CASELIST)
    319 					{
    320 						// \note Case list file is not open until we are in test package.
    321 						if (isLeaf)
    322 							fprintf(m_testCaseListFile, "TEST: %s\n", nodePath.c_str());
    323 						else if (node->getNodeType() != NODETYPE_ROOT)
    324 							fprintf(m_testCaseListFile, "GROUP: %s\n", nodePath.c_str());
    325 						iter.setState(isLeaf ? NodeIter::STATE_FINISH : NodeIter::STATE_TRAVERSE_CHILDREN);
    326 					}
    327 
    328 					break;
    329 				}
    330 
    331 				case NodeIter::STATE_EXECUTE_TEST:
    332 				{
    333 					// Touch the watchdog.
    334 					m_testCtx.touchWatchdog();
    335 
    336 					// Iterate the sub-case.
    337 					TestCase::IterateResult iterateResult = m_testCaseWrapper->iterateTestCase(static_cast<TestCase*>(node));
    338 
    339 					if (iterateResult == TestCase::STOP)
    340 						iter.setState(NodeIter::STATE_FINISH);
    341 
    342 					return true; // return after each iteration (when another iteration follows).
    343 				}
    344 
    345 				case NodeIter::STATE_TRAVERSE_CHILDREN:
    346 				{
    347 					int numChildren = (int)iter.children.size();
    348 					if (++iter.curChildNdx < numChildren)
    349 					{
    350 						// Push child to stack.
    351 						TestNode* childNode = iter.children[iter.curChildNdx];
    352 						m_sessionStack.push_back(NodeIter(childNode));
    353 					}
    354 					else
    355 						iter.setState(NodeIter::STATE_FINISH);
    356 
    357 					break;
    358 				}
    359 
    360 				case NodeIter::STATE_FINISH:
    361 				{
    362 					if (m_cmdLine.getRunMode() == RUNMODE_DUMP_XML_CASELIST)
    363 					{
    364 						if (node->getNodeType() != NODETYPE_ROOT && node->getNodeType() != NODETYPE_PACKAGE)
    365 							qpXmlWriter_endElement(m_testCaseListWriter, "TestCase");
    366 					}
    367 
    368 					// Leave node.
    369 					switch (node->getNodeType())
    370 					{
    371 						case NODETYPE_ROOT:				/* nada */											break;
    372 						case NODETYPE_PACKAGE:			leaveTestPackage(static_cast<TestPackage*>(node));	break;
    373 						case NODETYPE_GROUP:			leaveGroupNode(static_cast<TestCaseGroup*>(node));	break;
    374 						case NODETYPE_ACCURACY:
    375 						case NODETYPE_CAPABILITY:
    376 						case NODETYPE_PERFORMANCE:		/* fall-thru */
    377 						case NODETYPE_SELF_VALIDATE:	leaveTestCase(static_cast<TestCase*>(node));		break;
    378 						default: DE_ASSERT(false);
    379 					}
    380 
    381 					m_sessionStack.pop_back();
    382 
    383 					// Return if execution should abort.
    384 					if (m_abortSession)
    385 						return false;
    386 
    387 					// Otherwise continue iterating.
    388 					break;
    389 				}
    390 
    391 				default:
    392 					DE_ASSERT(DE_FALSE);
    393 					break;
    394 			}
    395 		}
    396 	}
    397 	catch (const std::exception& e)
    398 	{
    399 		print("TestExecutor::iterateSession(): Caught unhandled %s: %s\n", typeid(e).name(), e.what());
    400 		throw;
    401 	}
    402 
    403 	m_result.isComplete = true;
    404 	return false;
    405 }
    406 
    407 } // tcu
    408