xref: /aosp_15_r20/external/deqp/executor/xeBatchExecutor.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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