• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
isExecutedInBatch(const BatchResult * batchResult,const TestCase * testCase)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 
computeExecuteSet(TestSet & executeSet,const TestNode * root,const TestSet & testSet,const BatchResult * batchResult)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 
computeBatchRequest(TestSet & requestSet,const TestSet & executeSet,const TestNode * root,int maxCasesInSet)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 
removeExecuted(TestSet & set,const TestNode * root,const BatchResult * batchResult)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 
BatchExecutorLogHandler(BatchResult * batchResult)124 BatchExecutorLogHandler::BatchExecutorLogHandler (BatchResult* batchResult)
125 	: m_batchResult(batchResult)
126 {
127 }
128 
~BatchExecutorLogHandler(void)129 BatchExecutorLogHandler::~BatchExecutorLogHandler (void)
130 {
131 }
132 
setSessionInfo(const SessionInfo & sessionInfo)133 void BatchExecutorLogHandler::setSessionInfo (const SessionInfo& sessionInfo)
134 {
135 	m_batchResult->getSessionInfo() = sessionInfo;
136 }
137 
startTestCaseResult(const char * casePath)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 
testCaseResultUpdated(const TestCaseResultPtr &)147 void BatchExecutorLogHandler::testCaseResultUpdated (const TestCaseResultPtr&)
148 {
149 }
150 
testCaseResultComplete(const TestCaseResultPtr & result)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 
BatchExecutor(const TargetConfiguration & config,CommLink * commLink,const TestNode * root,const TestSet & testSet,BatchResult * batchResult,InfoLog * infoLog)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 
~BatchExecutor(void)170 BatchExecutor::~BatchExecutor (void)
171 {
172 }
173 
run(void)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 
cancel(void)227 void BatchExecutor::cancel (void)
228 {
229 	m_state = STATE_FINISHED;
230 	m_dispatcher.cancel();
231 }
232 
onStateChanged(CommLinkState state,const char * message)233 void BatchExecutor::onStateChanged (CommLinkState state, const char* message)
234 {
235 	switch (state)
236 	{
237 		case COMMLINKSTATE_READY:
238 		case COMMLINKSTATE_TEST_PROCESS_LAUNCHING:
239 		case COMMLINKSTATE_TEST_PROCESS_RUNNING:
240 			break; // Ignore.
241 
242 		case COMMLINKSTATE_TEST_PROCESS_FINISHED:
243 		{
244 			// Feed end of string to parser. This terminates open test case if such exists.
245 			{
246 				deUint8 eos = 0;
247 				onTestLogData(&eos, 1);
248 			}
249 
250 			int numExecuted = removeExecuted(m_casesToExecute, m_root, m_batchResult);
251 
252 			// \note No new batch is launched if no cases were executed in last one. Otherwise excutor
253 			//       could end up in infinite loop.
254 			if (!m_casesToExecute.empty() && numExecuted > 0)
255 			{
256 				// Reset state and start batch.
257 				m_testLogParser.reset();
258 
259 				m_commLink->reset();
260 				XE_CHECK(m_commLink->getState() == COMMLINKSTATE_READY);
261 
262 				TestSet batchRequest;
263 				computeBatchRequest(batchRequest, m_casesToExecute, m_root, m_config.maxCasesPerSession);
264 				launchTestSet(batchRequest);
265 			}
266 			else
267 				m_state = STATE_FINISHED;
268 
269 			break;
270 		}
271 
272 		case COMMLINKSTATE_TEST_PROCESS_LAUNCH_FAILED:
273 			printf("Failed to start test process: '%s'\n", message);
274 			m_state = STATE_FINISHED;
275 			break;
276 
277 		case COMMLINKSTATE_ERROR:
278 			printf("CommLink error: '%s'\n", message);
279 			m_state = STATE_FINISHED;
280 			break;
281 
282 		default:
283 			XE_FAIL("Unknown state");
284 	}
285 }
286 
onTestLogData(const deUint8 * bytes,size_t numBytes)287 void BatchExecutor::onTestLogData (const deUint8* bytes, size_t numBytes)
288 {
289 	try
290 	{
291 		m_testLogParser.parse(bytes, numBytes);
292 	}
293 	catch (const ParseError& e)
294 	{
295 		// \todo [2012-07-06 pyry] Log error.
296 		DE_UNREF(e);
297 	}
298 }
299 
onInfoLogData(const deUint8 * bytes,size_t numBytes)300 void BatchExecutor::onInfoLogData (const deUint8* bytes, size_t numBytes)
301 {
302 	if (numBytes > 0 && m_infoLog)
303 		m_infoLog->append(bytes, numBytes);
304 }
305 
writeCaseListNode(std::ostream & str,const TestNode * node,const TestSet & testSet)306 static void writeCaseListNode (std::ostream& str, const TestNode* node, const TestSet& testSet)
307 {
308 	DE_ASSERT(testSet.hasNode(node));
309 
310 	TestNodeType nodeType = node->getNodeType();
311 
312 	if (nodeType != TESTNODETYPE_ROOT)
313 		str << node->getName();
314 
315 	if (nodeType == TESTNODETYPE_ROOT || nodeType == TESTNODETYPE_GROUP)
316 	{
317 		const TestGroup*	group	= static_cast<const TestGroup*>(node);
318 		bool				isFirst	= true;
319 
320 		str << "{";
321 
322 		for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
323 		{
324 			const TestNode* child = group->getChild(ndx);
325 
326 			if (testSet.hasNode(child))
327 			{
328 				if (!isFirst)
329 					str << ",";
330 
331 				writeCaseListNode(str, child, testSet);
332 				isFirst = false;
333 			}
334 		}
335 
336 		str << "}";
337 	}
338 }
339 
launchTestSet(const TestSet & testSet)340 void BatchExecutor::launchTestSet (const TestSet& testSet)
341 {
342 	std::ostringstream caseList;
343 	XE_CHECK(testSet.hasNode(m_root));
344 	XE_CHECK(m_root->getNodeType() == TESTNODETYPE_ROOT);
345 	writeCaseListNode(caseList, m_root, testSet);
346 
347 	m_commLink->startTestProcess(m_config.binaryName.c_str(), m_config.cmdLineArgs.c_str(), m_config.workingDir.c_str(), caseList.str().c_str());
348 }
349 
enqueueStateChanged(void * userPtr,CommLinkState state,const char * message)350 void BatchExecutor::enqueueStateChanged (void* userPtr, CommLinkState state, const char* message)
351 {
352 	BatchExecutor*	executor	= static_cast<BatchExecutor*>(userPtr);
353 	CallWriter		writer		(&executor->m_dispatcher, BatchExecutor::dispatchStateChanged);
354 
355 	writer << executor
356 		   << state
357 		   << message;
358 
359 	writer.enqueue();
360 }
361 
enqueueTestLogData(void * userPtr,const deUint8 * bytes,size_t numBytes)362 void BatchExecutor::enqueueTestLogData (void* userPtr, const deUint8* bytes, size_t numBytes)
363 {
364 	BatchExecutor*	executor	= static_cast<BatchExecutor*>(userPtr);
365 	CallWriter		writer		(&executor->m_dispatcher, BatchExecutor::dispatchTestLogData);
366 
367 	writer << executor
368 		   << numBytes;
369 
370 	writer.write(bytes, numBytes);
371 	writer.enqueue();
372 }
373 
enqueueInfoLogData(void * userPtr,const deUint8 * bytes,size_t numBytes)374 void BatchExecutor::enqueueInfoLogData (void* userPtr, const deUint8* bytes, size_t numBytes)
375 {
376 	BatchExecutor*	executor	= static_cast<BatchExecutor*>(userPtr);
377 	CallWriter		writer		(&executor->m_dispatcher, BatchExecutor::dispatchInfoLogData);
378 
379 	writer << executor
380 		   << numBytes;
381 
382 	writer.write(bytes, numBytes);
383 	writer.enqueue();
384 }
385 
dispatchStateChanged(CallReader & data)386 void BatchExecutor::dispatchStateChanged (CallReader& data)
387 {
388 	BatchExecutor*	executor	= DE_NULL;
389 	CommLinkState	state		= COMMLINKSTATE_LAST;
390 	std::string		message;
391 
392 	data >> executor
393 		 >> state
394 		 >> message;
395 
396 	executor->onStateChanged(state, message.c_str());
397 }
398 
dispatchTestLogData(CallReader & data)399 void BatchExecutor::dispatchTestLogData (CallReader& data)
400 {
401 	BatchExecutor*	executor	= DE_NULL;
402 	size_t			numBytes;
403 
404 	data >> executor
405 		 >> numBytes;
406 
407 	executor->onTestLogData(data.getDataBlock(numBytes), numBytes);
408 }
409 
dispatchInfoLogData(CallReader & data)410 void BatchExecutor::dispatchInfoLogData (CallReader& data)
411 {
412 	BatchExecutor*	executor	= DE_NULL;
413 	size_t			numBytes;
414 
415 	data >> executor
416 		 >> numBytes;
417 
418 	executor->onInfoLogData(data.getDataBlock(numBytes), numBytes);
419 }
420 
421 } // xe
422