Home | History | Annotate | Download | only in executor
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Test Executor
      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 Test batch executor.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xeBatchExecutor.hpp"
     25 #include "xeTestResultParser.hpp"
     26 
     27 #include <sstream>
     28 #include <cstdio>
     29 
     30 namespace xe
     31 {
     32 
     33 using std::string;
     34 using std::vector;
     35 
     36 enum
     37 {
     38 	TEST_LOG_TMP_BUFFER_SIZE	= 1024,
     39 	INFO_LOG_TMP_BUFFER_SIZE	= 256
     40 };
     41 
     42 // \todo [2012-11-01 pyry] Update execute set in handler.
     43 
     44 static inline bool isExecutedInBatch (const BatchResult* batchResult, const TestCase* testCase)
     45 {
     46 	std::string fullPath;
     47 	testCase->getFullPath(fullPath);
     48 
     49 	if (batchResult->hasTestCaseResult(fullPath.c_str()))
     50 	{
     51 		ConstTestCaseResultPtr data = batchResult->getTestCaseResult(fullPath.c_str());
     52 		return data->getStatusCode() != TESTSTATUSCODE_PENDING && data->getStatusCode() != TESTSTATUSCODE_RUNNING;
     53 	}
     54 	else
     55 		return false;
     56 }
     57 
     58 // \todo [2012-06-19 pyry] These can be optimized using TestSetIterator (once implemented)
     59 
     60 static void computeExecuteSet (TestSet& executeSet, const TestNode* root, const TestSet& testSet, const BatchResult* batchResult)
     61 {
     62 	ConstTestNodeIterator	iter	= ConstTestNodeIterator::begin(root);
     63 	ConstTestNodeIterator	end		= ConstTestNodeIterator::end(root);
     64 
     65 	for (; iter != end; ++iter)
     66 	{
     67 		const TestNode* node = *iter;
     68 
     69 		if (node->getNodeType() == TESTNODETYPE_TEST_CASE && testSet.hasNode(node))
     70 		{
     71 			const TestCase* testCase = static_cast<const TestCase*>(node);
     72 
     73 			if (!isExecutedInBatch(batchResult, testCase))
     74 				executeSet.addCase(testCase);
     75 		}
     76 	}
     77 }
     78 
     79 static void computeBatchRequest (TestSet& requestSet, const TestSet& executeSet, const TestNode* root, int maxCasesInSet)
     80 {
     81 	ConstTestNodeIterator	iter		= ConstTestNodeIterator::begin(root);
     82 	ConstTestNodeIterator	end			= ConstTestNodeIterator::end(root);
     83 	int						numCases	= 0;
     84 
     85 	for (; (iter != end) && (numCases < maxCasesInSet); ++iter)
     86 	{
     87 		const TestNode* node = *iter;
     88 
     89 		if (node->getNodeType() == TESTNODETYPE_TEST_CASE && executeSet.hasNode(node))
     90 		{
     91 			const TestCase* testCase = static_cast<const TestCase*>(node);
     92 			requestSet.addCase(testCase);
     93 			numCases += 1;
     94 		}
     95 	}
     96 }
     97 
     98 static int removeExecuted (TestSet& set, const TestNode* root, const BatchResult* batchResult)
     99 {
    100 	TestSet					oldSet		(set);
    101 	ConstTestNodeIterator	iter		= ConstTestNodeIterator::begin(root);
    102 	ConstTestNodeIterator	end			= ConstTestNodeIterator::end(root);
    103 	int						numRemoved	= 0;
    104 
    105 	for (; iter != end; ++iter)
    106 	{
    107 		const TestNode* node = *iter;
    108 
    109 		if (node->getNodeType() == TESTNODETYPE_TEST_CASE && oldSet.hasNode(node))
    110 		{
    111 			const TestCase* testCase = static_cast<const TestCase*>(node);
    112 
    113 			if (isExecutedInBatch(batchResult, testCase))
    114 			{
    115 				set.removeCase(testCase);
    116 				numRemoved += 1;
    117 			}
    118 		}
    119 	}
    120 
    121 	return numRemoved;
    122 }
    123 
    124 BatchExecutorLogHandler::BatchExecutorLogHandler (BatchResult* batchResult)
    125 	: m_batchResult(batchResult)
    126 {
    127 }
    128 
    129 BatchExecutorLogHandler::~BatchExecutorLogHandler (void)
    130 {
    131 }
    132 
    133 void BatchExecutorLogHandler::setSessionInfo (const SessionInfo& sessionInfo)
    134 {
    135 	m_batchResult->getSessionInfo() = sessionInfo;
    136 }
    137 
    138 TestCaseResultPtr BatchExecutorLogHandler::startTestCaseResult (const char* casePath)
    139 {
    140 	// \todo [2012-11-01 pyry] What to do with duplicate results?
    141 	if (m_batchResult->hasTestCaseResult(casePath))
    142 		return m_batchResult->getTestCaseResult(casePath);
    143 	else
    144 		return m_batchResult->createTestCaseResult(casePath);
    145 }
    146 
    147 void BatchExecutorLogHandler::testCaseResultUpdated (const TestCaseResultPtr&)
    148 {
    149 }
    150 
    151 void BatchExecutorLogHandler::testCaseResultComplete (const TestCaseResultPtr& result)
    152 {
    153 	// \todo [2012-11-01 pyry] Remove from execute set here instead of updating it between sessions.
    154 	printf("%s\n", result->getTestCasePath());
    155 }
    156 
    157 BatchExecutor::BatchExecutor (const TargetConfiguration& config, CommLink* commLink, const TestNode* root, const TestSet& testSet, BatchResult* batchResult, InfoLog* infoLog)
    158 	: m_config			(config)
    159 	, m_commLink		(commLink)
    160 	, m_root			(root)
    161 	, m_testSet			(testSet)
    162 	, m_logHandler		(batchResult)
    163 	, m_batchResult		(batchResult)
    164 	, m_infoLog			(infoLog)
    165 	, m_state			(STATE_NOT_STARTED)
    166 	, m_testLogParser	(&m_logHandler)
    167 {
    168 }
    169 
    170 BatchExecutor::~BatchExecutor (void)
    171 {
    172 }
    173 
    174 void BatchExecutor::run (void)
    175 {
    176 	XE_CHECK(m_state == STATE_NOT_STARTED);
    177 
    178 	// Check commlink state.
    179 	{
    180 		CommLinkState	commState	= COMMLINKSTATE_LAST;
    181 		std::string		stateStr	= "";
    182 
    183 		commState = m_commLink->getState(stateStr);
    184 
    185 		if (commState == COMMLINKSTATE_ERROR)
    186 		{
    187 			// Report error.
    188 			XE_FAIL((string("CommLink error: '") + stateStr + "'").c_str());
    189 		}
    190 		else if (commState != COMMLINKSTATE_READY)
    191 			XE_FAIL("CommLink is not ready");
    192 	}
    193 
    194 	// Compute initial execute set.
    195 	computeExecuteSet(m_casesToExecute, m_root, m_testSet, m_batchResult);
    196 
    197 	// Register callbacks.
    198 	m_commLink->setCallbacks(enqueueStateChanged, enqueueTestLogData, enqueueInfoLogData, this);
    199 
    200 	try
    201 	{
    202 		if (!m_casesToExecute.empty())
    203 		{
    204 			TestSet batchRequest;
    205 			computeBatchRequest(batchRequest, m_casesToExecute, m_root, m_config.maxCasesPerSession);
    206 			launchTestSet(batchRequest);
    207 
    208 			m_state = STATE_STARTED;
    209 		}
    210 		else
    211 			m_state = STATE_FINISHED;
    212 
    213 		// Run handler loop until we are finished.
    214 		while (m_state != STATE_FINISHED)
    215 			m_dispatcher.callNext();
    216 	}
    217 	catch (...)
    218 	{
    219 		m_commLink->setCallbacks(DE_NULL, DE_NULL, DE_NULL, DE_NULL);
    220 		throw;
    221 	}
    222 
    223 	// De-register callbacks.
    224 	m_commLink->setCallbacks(DE_NULL, DE_NULL, DE_NULL, DE_NULL);
    225 }
    226 
    227 void BatchExecutor::onStateChanged (CommLinkState state, const char* message)
    228 {
    229 	switch (state)
    230 	{
    231 		case COMMLINKSTATE_READY:
    232 		case COMMLINKSTATE_TEST_PROCESS_LAUNCHING:
    233 		case COMMLINKSTATE_TEST_PROCESS_RUNNING:
    234 			break; // Ignore.
    235 
    236 		case COMMLINKSTATE_TEST_PROCESS_FINISHED:
    237 		{
    238 			// Feed end of string to parser. This terminates open test case if such exists.
    239 			{
    240 				deUint8 eos = 0;
    241 				onTestLogData(&eos, 1);
    242 			}
    243 
    244 			int numExecuted = removeExecuted(m_casesToExecute, m_root, m_batchResult);
    245 
    246 			// \note No new batch is launched if no cases were executed in last one. Otherwise excutor
    247 			//       could end up in infinite loop.
    248 			if (!m_casesToExecute.empty() && numExecuted > 0)
    249 			{
    250 				// Reset state and start batch.
    251 				m_testLogParser.reset();
    252 
    253 				m_commLink->reset();
    254 				XE_CHECK(m_commLink->getState() == COMMLINKSTATE_READY);
    255 
    256 				TestSet batchRequest;
    257 				computeBatchRequest(batchRequest, m_casesToExecute, m_root, m_config.maxCasesPerSession);
    258 				launchTestSet(batchRequest);
    259 			}
    260 			else
    261 				m_state = STATE_FINISHED;
    262 
    263 			break;
    264 		}
    265 
    266 		case COMMLINKSTATE_TEST_PROCESS_LAUNCH_FAILED:
    267 			printf("Failed to start test process: '%s'\n", message);
    268 			m_state = STATE_FINISHED;
    269 			break;
    270 
    271 		case COMMLINKSTATE_ERROR:
    272 			printf("CommLink error: '%s'\n", message);
    273 			m_state = STATE_FINISHED;
    274 			break;
    275 
    276 		default:
    277 			XE_FAIL("Unknown state");
    278 	}
    279 }
    280 
    281 void BatchExecutor::onTestLogData (const deUint8* bytes, int numBytes)
    282 {
    283 	try
    284 	{
    285 		m_testLogParser.parse(bytes, numBytes);
    286 	}
    287 	catch (const ParseError& e)
    288 	{
    289 		// \todo [2012-07-06 pyry] Log error.
    290 		DE_UNREF(e);
    291 	}
    292 }
    293 
    294 void BatchExecutor::onInfoLogData (const deUint8* bytes, int numBytes)
    295 {
    296 	if (numBytes > 0 && m_infoLog)
    297 		m_infoLog->append(bytes, numBytes);
    298 }
    299 
    300 static void writeCaseListNode (std::ostream& str, const TestNode* node, const TestSet& testSet)
    301 {
    302 	DE_ASSERT(testSet.hasNode(node));
    303 
    304 	TestNodeType nodeType = node->getNodeType();
    305 
    306 	if (nodeType != TESTNODETYPE_ROOT)
    307 		str << node->getName();
    308 
    309 	if (nodeType == TESTNODETYPE_ROOT || nodeType == TESTNODETYPE_GROUP)
    310 	{
    311 		const TestGroup*	group	= static_cast<const TestGroup*>(node);
    312 		bool				isFirst	= true;
    313 
    314 		str << "{";
    315 
    316 		for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
    317 		{
    318 			const TestNode* child = group->getChild(ndx);
    319 
    320 			if (testSet.hasNode(child))
    321 			{
    322 				if (!isFirst)
    323 					str << ",";
    324 
    325 				writeCaseListNode(str, child, testSet);
    326 				isFirst = false;
    327 			}
    328 		}
    329 
    330 		str << "}";
    331 	}
    332 }
    333 
    334 void BatchExecutor::launchTestSet (const TestSet& testSet)
    335 {
    336 	std::ostringstream caseList;
    337 	XE_CHECK(testSet.hasNode(m_root));
    338 	XE_CHECK(m_root->getNodeType() == TESTNODETYPE_ROOT);
    339 	writeCaseListNode(caseList, m_root, testSet);
    340 
    341 	m_commLink->startTestProcess(m_config.binaryName.c_str(), m_config.cmdLineArgs.c_str(), m_config.workingDir.c_str(), caseList.str().c_str());
    342 }
    343 
    344 void BatchExecutor::enqueueStateChanged (void* userPtr, CommLinkState state, const char* message)
    345 {
    346 	BatchExecutor*	executor	= static_cast<BatchExecutor*>(userPtr);
    347 	CallWriter		writer		(&executor->m_dispatcher, BatchExecutor::dispatchStateChanged);
    348 
    349 	writer << executor
    350 		   << state
    351 		   << message;
    352 
    353 	writer.enqueue();
    354 }
    355 
    356 void BatchExecutor::enqueueTestLogData (void* userPtr, const deUint8* bytes, int numBytes)
    357 {
    358 	BatchExecutor*	executor	= static_cast<BatchExecutor*>(userPtr);
    359 	CallWriter		writer		(&executor->m_dispatcher, BatchExecutor::dispatchTestLogData);
    360 
    361 	writer << executor
    362 		   << numBytes;
    363 
    364 	writer.write(bytes, numBytes);
    365 	writer.enqueue();
    366 }
    367 
    368 void BatchExecutor::enqueueInfoLogData (void* userPtr, const deUint8* bytes, int numBytes)
    369 {
    370 	BatchExecutor*	executor	= static_cast<BatchExecutor*>(userPtr);
    371 	CallWriter		writer		(&executor->m_dispatcher, BatchExecutor::dispatchInfoLogData);
    372 
    373 	writer << executor
    374 		   << numBytes;
    375 
    376 	writer.write(bytes, numBytes);
    377 	writer.enqueue();
    378 }
    379 
    380 void BatchExecutor::dispatchStateChanged (CallReader data)
    381 {
    382 	BatchExecutor*	executor	= DE_NULL;
    383 	CommLinkState	state		= COMMLINKSTATE_LAST;
    384 	std::string		message;
    385 
    386 	data >> executor
    387 		 >> state
    388 		 >> message;
    389 
    390 	executor->onStateChanged(state, message.c_str());
    391 }
    392 
    393 void BatchExecutor::dispatchTestLogData (CallReader data)
    394 {
    395 	BatchExecutor*	executor	= DE_NULL;
    396 	int				numBytes;
    397 
    398 	data >> executor
    399 		 >> numBytes;
    400 
    401 	executor->onTestLogData(data.getDataBlock(numBytes), numBytes);
    402 }
    403 
    404 void BatchExecutor::dispatchInfoLogData (CallReader data)
    405 {
    406 	BatchExecutor*	executor	= DE_NULL;
    407 	int				numBytes;
    408 
    409 	data >> executor
    410 		 >> numBytes;
    411 
    412 	executor->onInfoLogData(data.getDataBlock(numBytes), numBytes);
    413 }
    414 
    415 } // xe
    416