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 30namespace xe 31{ 32 33using std::string; 34using std::vector; 35 36enum 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 44static 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 60static 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 79static 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 98static 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 124BatchExecutorLogHandler::BatchExecutorLogHandler (BatchResult* batchResult) 125 : m_batchResult(batchResult) 126{ 127} 128 129BatchExecutorLogHandler::~BatchExecutorLogHandler (void) 130{ 131} 132 133void BatchExecutorLogHandler::setSessionInfo (const SessionInfo& sessionInfo) 134{ 135 m_batchResult->getSessionInfo() = sessionInfo; 136} 137 138TestCaseResultPtr 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 147void BatchExecutorLogHandler::testCaseResultUpdated (const TestCaseResultPtr&) 148{ 149} 150 151void 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 157BatchExecutor::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 170BatchExecutor::~BatchExecutor (void) 171{ 172} 173 174void 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 227void BatchExecutor::cancel (void) 228{ 229 m_state = STATE_FINISHED; 230 m_dispatcher.cancel(); 231} 232 233void 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 287void 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 300void BatchExecutor::onInfoLogData (const deUint8* bytes, size_t numBytes) 301{ 302 if (numBytes > 0 && m_infoLog) 303 m_infoLog->append(bytes, numBytes); 304} 305 306static 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 340void 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 350void 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 362void 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 374void 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 386void 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 399void 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 410void 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