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,
61 const BatchResult *batchResult)
62 {
63 ConstTestNodeIterator iter = ConstTestNodeIterator::begin(root);
64 ConstTestNodeIterator end = ConstTestNodeIterator::end(root);
65
66 for (; iter != end; ++iter)
67 {
68 const TestNode *node = *iter;
69
70 if (node->getNodeType() == TESTNODETYPE_TEST_CASE && testSet.hasNode(node))
71 {
72 const TestCase *testCase = static_cast<const TestCase *>(node);
73
74 if (!isExecutedInBatch(batchResult, testCase))
75 executeSet.addCase(testCase);
76 }
77 }
78 }
79
computeBatchRequest(TestSet & requestSet,const TestSet & executeSet,const TestNode * root,int maxCasesInSet)80 static void computeBatchRequest(TestSet &requestSet, const TestSet &executeSet, const TestNode *root, int maxCasesInSet)
81 {
82 ConstTestNodeIterator iter = ConstTestNodeIterator::begin(root);
83 ConstTestNodeIterator end = ConstTestNodeIterator::end(root);
84 int numCases = 0;
85
86 for (; (iter != end) && (numCases < maxCasesInSet); ++iter)
87 {
88 const TestNode *node = *iter;
89
90 if (node->getNodeType() == TESTNODETYPE_TEST_CASE && executeSet.hasNode(node))
91 {
92 const TestCase *testCase = static_cast<const TestCase *>(node);
93 requestSet.addCase(testCase);
94 numCases += 1;
95 }
96 }
97 }
98
removeExecuted(TestSet & set,const TestNode * root,const BatchResult * batchResult)99 static int removeExecuted(TestSet &set, const TestNode *root, const BatchResult *batchResult)
100 {
101 TestSet oldSet(set);
102 ConstTestNodeIterator iter = ConstTestNodeIterator::begin(root);
103 ConstTestNodeIterator end = ConstTestNodeIterator::end(root);
104 int numRemoved = 0;
105
106 for (; iter != end; ++iter)
107 {
108 const TestNode *node = *iter;
109
110 if (node->getNodeType() == TESTNODETYPE_TEST_CASE && oldSet.hasNode(node))
111 {
112 const TestCase *testCase = static_cast<const TestCase *>(node);
113
114 if (isExecutedInBatch(batchResult, testCase))
115 {
116 set.removeCase(testCase);
117 numRemoved += 1;
118 }
119 }
120 }
121
122 return numRemoved;
123 }
124
BatchExecutorLogHandler(BatchResult * batchResult)125 BatchExecutorLogHandler::BatchExecutorLogHandler(BatchResult *batchResult) : 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,
158 const TestSet &testSet, BatchResult *batchResult, InfoLog *infoLog)
159 : m_config(config)
160 , m_commLink(commLink)
161 , m_root(root)
162 , m_testSet(testSet)
163 , m_logHandler(batchResult)
164 , m_batchResult(batchResult)
165 , m_infoLog(infoLog)
166 , m_state(STATE_NOT_STARTED)
167 , m_testLogParser(&m_logHandler)
168 {
169 }
170
~BatchExecutor(void)171 BatchExecutor::~BatchExecutor(void)
172 {
173 }
174
run(void)175 void BatchExecutor::run(void)
176 {
177 XE_CHECK(m_state == STATE_NOT_STARTED);
178
179 // Check commlink state.
180 {
181 CommLinkState commState = COMMLINKSTATE_LAST;
182 std::string stateStr = "";
183
184 commState = m_commLink->getState(stateStr);
185
186 if (commState == COMMLINKSTATE_ERROR)
187 {
188 // Report error.
189 XE_FAIL((string("CommLink error: '") + stateStr + "'").c_str());
190 }
191 else if (commState != COMMLINKSTATE_READY)
192 XE_FAIL("CommLink is not ready");
193 }
194
195 // Compute initial execute set.
196 computeExecuteSet(m_casesToExecute, m_root, m_testSet, m_batchResult);
197
198 // Register callbacks.
199 m_commLink->setCallbacks(enqueueStateChanged, enqueueTestLogData, enqueueInfoLogData, this);
200
201 try
202 {
203 if (!m_casesToExecute.empty())
204 {
205 TestSet batchRequest;
206 computeBatchRequest(batchRequest, m_casesToExecute, m_root, m_config.maxCasesPerSession);
207 launchTestSet(batchRequest);
208
209 m_state = STATE_STARTED;
210 }
211 else
212 m_state = STATE_FINISHED;
213
214 // Run handler loop until we are finished.
215 while (m_state != STATE_FINISHED)
216 m_dispatcher.callNext();
217 }
218 catch (...)
219 {
220 m_commLink->setCallbacks(DE_NULL, DE_NULL, DE_NULL, DE_NULL);
221 throw;
222 }
223
224 // De-register callbacks.
225 m_commLink->setCallbacks(DE_NULL, DE_NULL, DE_NULL, DE_NULL);
226 }
227
cancel(void)228 void BatchExecutor::cancel(void)
229 {
230 m_state = STATE_FINISHED;
231 m_dispatcher.cancel();
232 }
233
onStateChanged(CommLinkState state,const char * message)234 void BatchExecutor::onStateChanged(CommLinkState state, const char *message)
235 {
236 switch (state)
237 {
238 case COMMLINKSTATE_READY:
239 case COMMLINKSTATE_TEST_PROCESS_LAUNCHING:
240 case COMMLINKSTATE_TEST_PROCESS_RUNNING:
241 break; // Ignore.
242
243 case COMMLINKSTATE_TEST_PROCESS_FINISHED:
244 {
245 // Feed end of string to parser. This terminates open test case if such exists.
246 {
247 uint8_t eos = 0;
248 onTestLogData(&eos, 1);
249 }
250
251 int numExecuted = removeExecuted(m_casesToExecute, m_root, m_batchResult);
252
253 // \note No new batch is launched if no cases were executed in last one. Otherwise excutor
254 // could end up in infinite loop.
255 if (!m_casesToExecute.empty() && numExecuted > 0)
256 {
257 // Reset state and start batch.
258 m_testLogParser.reset();
259
260 m_commLink->reset();
261 XE_CHECK(m_commLink->getState() == COMMLINKSTATE_READY);
262
263 TestSet batchRequest;
264 computeBatchRequest(batchRequest, m_casesToExecute, m_root, m_config.maxCasesPerSession);
265 launchTestSet(batchRequest);
266 }
267 else
268 m_state = STATE_FINISHED;
269
270 break;
271 }
272
273 case COMMLINKSTATE_TEST_PROCESS_LAUNCH_FAILED:
274 printf("Failed to start test process: '%s'\n", message);
275 m_state = STATE_FINISHED;
276 break;
277
278 case COMMLINKSTATE_ERROR:
279 printf("CommLink error: '%s'\n", message);
280 m_state = STATE_FINISHED;
281 break;
282
283 default:
284 XE_FAIL("Unknown state");
285 }
286 }
287
onTestLogData(const uint8_t * bytes,size_t numBytes)288 void BatchExecutor::onTestLogData(const uint8_t *bytes, size_t numBytes)
289 {
290 try
291 {
292 m_testLogParser.parse(bytes, numBytes);
293 }
294 catch (const ParseError &e)
295 {
296 // \todo [2012-07-06 pyry] Log error.
297 DE_UNREF(e);
298 }
299 }
300
onInfoLogData(const uint8_t * bytes,size_t numBytes)301 void BatchExecutor::onInfoLogData(const uint8_t *bytes, size_t numBytes)
302 {
303 if (numBytes > 0 && m_infoLog)
304 m_infoLog->append(bytes, numBytes);
305 }
306
writeCaseListNode(std::ostream & str,const TestNode * node,const TestSet & testSet)307 static void writeCaseListNode(std::ostream &str, const TestNode *node, const TestSet &testSet)
308 {
309 DE_ASSERT(testSet.hasNode(node));
310
311 TestNodeType nodeType = node->getNodeType();
312
313 if (nodeType != TESTNODETYPE_ROOT)
314 str << node->getName();
315
316 if (nodeType == TESTNODETYPE_ROOT || nodeType == TESTNODETYPE_GROUP)
317 {
318 const TestGroup *group = static_cast<const TestGroup *>(node);
319 bool isFirst = true;
320
321 str << "{";
322
323 for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
324 {
325 const TestNode *child = group->getChild(ndx);
326
327 if (testSet.hasNode(child))
328 {
329 if (!isFirst)
330 str << ",";
331
332 writeCaseListNode(str, child, testSet);
333 isFirst = false;
334 }
335 }
336
337 str << "}";
338 }
339 }
340
launchTestSet(const TestSet & testSet)341 void BatchExecutor::launchTestSet(const TestSet &testSet)
342 {
343 std::ostringstream caseList;
344 XE_CHECK(testSet.hasNode(m_root));
345 XE_CHECK(m_root->getNodeType() == TESTNODETYPE_ROOT);
346 writeCaseListNode(caseList, m_root, testSet);
347
348 m_commLink->startTestProcess(m_config.binaryName.c_str(), m_config.cmdLineArgs.c_str(), m_config.workingDir.c_str(),
349 caseList.str().c_str());
350 }
351
enqueueStateChanged(void * userPtr,CommLinkState state,const char * message)352 void BatchExecutor::enqueueStateChanged(void *userPtr, CommLinkState state, const char *message)
353 {
354 BatchExecutor *executor = static_cast<BatchExecutor *>(userPtr);
355 CallWriter writer(&executor->m_dispatcher, BatchExecutor::dispatchStateChanged);
356
357 writer << executor << state << message;
358
359 writer.enqueue();
360 }
361
enqueueTestLogData(void * userPtr,const uint8_t * bytes,size_t numBytes)362 void BatchExecutor::enqueueTestLogData(void *userPtr, const uint8_t *bytes, size_t numBytes)
363 {
364 BatchExecutor *executor = static_cast<BatchExecutor *>(userPtr);
365 CallWriter writer(&executor->m_dispatcher, BatchExecutor::dispatchTestLogData);
366
367 writer << executor << numBytes;
368
369 writer.write(bytes, numBytes);
370 writer.enqueue();
371 }
372
enqueueInfoLogData(void * userPtr,const uint8_t * bytes,size_t numBytes)373 void BatchExecutor::enqueueInfoLogData(void *userPtr, const uint8_t *bytes, size_t numBytes)
374 {
375 BatchExecutor *executor = static_cast<BatchExecutor *>(userPtr);
376 CallWriter writer(&executor->m_dispatcher, BatchExecutor::dispatchInfoLogData);
377
378 writer << executor << numBytes;
379
380 writer.write(bytes, numBytes);
381 writer.enqueue();
382 }
383
dispatchStateChanged(CallReader & data)384 void BatchExecutor::dispatchStateChanged(CallReader &data)
385 {
386 BatchExecutor *executor = DE_NULL;
387 CommLinkState state = COMMLINKSTATE_LAST;
388 std::string message;
389
390 data >> executor >> state >> message;
391
392 executor->onStateChanged(state, message.c_str());
393 }
394
dispatchTestLogData(CallReader & data)395 void BatchExecutor::dispatchTestLogData(CallReader &data)
396 {
397 BatchExecutor *executor = DE_NULL;
398 size_t numBytes;
399
400 data >> executor >> numBytes;
401
402 executor->onTestLogData(data.getDataBlock(numBytes), numBytes);
403 }
404
dispatchInfoLogData(CallReader & data)405 void BatchExecutor::dispatchInfoLogData(CallReader &data)
406 {
407 BatchExecutor *executor = DE_NULL;
408 size_t numBytes;
409
410 data >> executor >> numBytes;
411
412 executor->onInfoLogData(data.getDataBlock(numBytes), numBytes);
413 }
414
415 } // namespace xe
416