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
onStateChanged(CommLinkState state,const char * message)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
onTestLogData(const deUint8 * bytes,int numBytes)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
onInfoLogData(const deUint8 * bytes,int numBytes)294 void BatchExecutor::onInfoLogData (const deUint8* bytes, int numBytes)
295 {
296 if (numBytes > 0 && m_infoLog)
297 m_infoLog->append(bytes, numBytes);
298 }
299
writeCaseListNode(std::ostream & str,const TestNode * node,const TestSet & testSet)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
launchTestSet(const TestSet & testSet)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
enqueueStateChanged(void * userPtr,CommLinkState state,const char * message)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
enqueueTestLogData(void * userPtr,const deUint8 * bytes,int numBytes)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
enqueueInfoLogData(void * userPtr,const deUint8 * bytes,int numBytes)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
dispatchStateChanged(CallReader data)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
dispatchTestLogData(CallReader data)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
dispatchInfoLogData(CallReader data)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