1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 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 executor. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "tcuTestSessionExecutor.hpp" 25 #include "tcuCommandLine.hpp" 26 #include "tcuTestLog.hpp" 27 28 #include "deClock.h" 29 30 namespace tcu 31 { 32 33 using std::vector; 34 35 static qpTestCaseType nodeTypeToTestCaseType (TestNodeType nodeType) 36 { 37 switch (nodeType) 38 { 39 case NODETYPE_SELF_VALIDATE: return QP_TEST_CASE_TYPE_SELF_VALIDATE; 40 case NODETYPE_PERFORMANCE: return QP_TEST_CASE_TYPE_PERFORMANCE; 41 case NODETYPE_CAPABILITY: return QP_TEST_CASE_TYPE_CAPABILITY; 42 case NODETYPE_ACCURACY: return QP_TEST_CASE_TYPE_ACCURACY; 43 default: 44 DE_ASSERT(false); 45 return QP_TEST_CASE_TYPE_LAST; 46 } 47 } 48 49 TestSessionExecutor::TestSessionExecutor (TestPackageRoot& root, TestContext& testCtx) 50 : m_testCtx (testCtx) 51 , m_inflater (testCtx) 52 , m_caseListFilter (testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive())) 53 , m_iterator (root, m_inflater, *m_caseListFilter) 54 , m_state (STATE_TRAVERSE_HIERARCHY) 55 , m_abortSession (false) 56 , m_isInTestCase (false) 57 , m_testStartTime (0) 58 , m_packageStartTime (0) 59 { 60 } 61 62 TestSessionExecutor::~TestSessionExecutor (void) 63 { 64 } 65 66 bool TestSessionExecutor::iterate (void) 67 { 68 while (!m_abortSession) 69 { 70 switch (m_state) 71 { 72 case STATE_TRAVERSE_HIERARCHY: 73 { 74 const TestHierarchyIterator::State hierIterState = m_iterator.getState(); 75 76 if (hierIterState == TestHierarchyIterator::STATE_ENTER_NODE || 77 hierIterState == TestHierarchyIterator::STATE_LEAVE_NODE) 78 { 79 TestNode* const curNode = m_iterator.getNode(); 80 const TestNodeType nodeType = curNode->getNodeType(); 81 const bool isEnter = hierIterState == TestHierarchyIterator::STATE_ENTER_NODE; 82 83 switch (nodeType) 84 { 85 case NODETYPE_PACKAGE: 86 { 87 TestPackage* const testPackage = static_cast<TestPackage*>(curNode); 88 isEnter ? enterTestPackage(testPackage) : leaveTestPackage(testPackage); 89 break; 90 } 91 92 case NODETYPE_GROUP: 93 { 94 isEnter ? enterTestGroup(m_iterator.getNodePath()) : leaveTestGroup(m_iterator.getNodePath()); 95 break; // nada 96 } 97 98 case NODETYPE_SELF_VALIDATE: 99 case NODETYPE_PERFORMANCE: 100 case NODETYPE_CAPABILITY: 101 case NODETYPE_ACCURACY: 102 { 103 TestCase* const testCase = static_cast<TestCase*>(curNode); 104 105 if (isEnter) 106 { 107 if (enterTestCase(testCase, m_iterator.getNodePath())) 108 m_state = STATE_EXECUTE_TEST_CASE; 109 // else remain in TRAVERSING_HIERARCHY => node will be exited from in the next iteration 110 } 111 else 112 leaveTestCase(testCase); 113 114 break; 115 } 116 117 default: 118 DE_ASSERT(false); 119 break; 120 } 121 122 m_iterator.next(); 123 break; 124 } 125 else 126 { 127 DE_ASSERT(hierIterState == TestHierarchyIterator::STATE_FINISHED); 128 m_status.isComplete = true; 129 return false; 130 } 131 } 132 133 case STATE_EXECUTE_TEST_CASE: 134 { 135 DE_ASSERT(m_iterator.getState() == TestHierarchyIterator::STATE_LEAVE_NODE && 136 isTestNodeTypeExecutable(m_iterator.getNode()->getNodeType())); 137 138 TestCase* const testCase = static_cast<TestCase*>(m_iterator.getNode()); 139 const TestCase::IterateResult iterResult = iterateTestCase(testCase); 140 141 if (iterResult == TestCase::STOP) 142 m_state = STATE_TRAVERSE_HIERARCHY; 143 144 return true; 145 } 146 147 default: 148 DE_ASSERT(false); 149 break; 150 } 151 } 152 153 return false; 154 } 155 156 void TestSessionExecutor::enterTestPackage (TestPackage* testPackage) 157 { 158 // Create test case wrapper 159 DE_ASSERT(!m_caseExecutor); 160 m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor()); 161 m_packageStartTime = deGetMicroseconds(); 162 } 163 164 void TestSessionExecutor::leaveTestPackage (TestPackage* testPackage) 165 { 166 DE_UNREF(testPackage); 167 m_caseExecutor.clear(); 168 m_testCtx.getLog().startTestsCasesTime(); 169 170 { 171 const deInt64 duration = deGetMicroseconds() - m_packageStartTime; 172 m_packageStartTime = 0; 173 m_testCtx.getLog() << TestLog::Integer(testPackage->getName(), "Total tests case duration in microseconds", "us", QP_KEY_TAG_TIME, duration); 174 } 175 176 for(std::map<std::string, deUint64>::iterator it=m_groupsDurationTime.begin(); it != m_groupsDurationTime.end(); ++it) 177 m_testCtx.getLog() << TestLog::Integer(it->first, "The test group case duration in microseconds", "us", QP_KEY_TAG_TIME, it->second); 178 179 m_testCtx.getLog().endTestsCasesTime(); 180 } 181 182 void TestSessionExecutor::enterTestGroup (const std::string& casePath) 183 { 184 m_groupsDurationTime[casePath] = deGetMicroseconds(); 185 } 186 187 void TestSessionExecutor::leaveTestGroup (const std::string& casePath) 188 { 189 m_groupsDurationTime[casePath] = deGetMicroseconds() - m_groupsDurationTime[casePath]; 190 } 191 192 bool TestSessionExecutor::enterTestCase (TestCase* testCase, const std::string& casePath) 193 { 194 TestLog& log = m_testCtx.getLog(); 195 const qpTestCaseType caseType = nodeTypeToTestCaseType(testCase->getNodeType()); 196 bool initOk = false; 197 198 print("\nTest case '%s'..\n", casePath.c_str()); 199 200 m_testCtx.setTestResult(QP_TEST_RESULT_LAST, ""); 201 m_testCtx.setTerminateAfter(false); 202 log.startCase(casePath.c_str(), caseType); 203 204 m_isInTestCase = true; 205 m_testStartTime = deGetMicroseconds(); 206 207 try 208 { 209 m_caseExecutor->init(testCase, casePath); 210 initOk = true; 211 } 212 catch (const std::bad_alloc&) 213 { 214 DE_ASSERT(!initOk); 215 m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init"); 216 m_testCtx.setTerminateAfter(true); 217 } 218 catch (const tcu::TestException& e) 219 { 220 DE_ASSERT(!initOk); 221 DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST); 222 m_testCtx.setTestResult(e.getTestResult(), e.getMessage()); 223 m_testCtx.setTerminateAfter(e.isFatal()); 224 log << e; 225 } 226 catch (const tcu::Exception& e) 227 { 228 DE_ASSERT(!initOk); 229 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage()); 230 log << e; 231 } 232 233 DE_ASSERT(initOk || m_testCtx.getTestResult() != QP_TEST_RESULT_LAST); 234 235 return initOk; 236 } 237 238 void TestSessionExecutor::leaveTestCase (TestCase* testCase) 239 { 240 TestLog& log = m_testCtx.getLog(); 241 242 // De-init case. 243 try 244 { 245 m_caseExecutor->deinit(testCase); 246 } 247 catch (const tcu::Exception& e) 248 { 249 log << e << TestLog::Message << "Error in test case deinit, test program will terminate." << TestLog::EndMessage; 250 m_testCtx.setTerminateAfter(true); 251 } 252 253 { 254 const deInt64 duration = deGetMicroseconds()-m_testStartTime; 255 m_testStartTime = 0; 256 m_testCtx.getLog() << TestLog::Integer("TestDuration", "Test case duration in microseconds", "us", QP_KEY_TAG_TIME, duration); 257 } 258 259 { 260 const qpTestResult testResult = m_testCtx.getTestResult(); 261 const char* const testResultDesc = m_testCtx.getTestResultDesc(); 262 const bool terminateAfter = m_testCtx.getTerminateAfter(); 263 DE_ASSERT(testResult != QP_TEST_RESULT_LAST); 264 265 m_isInTestCase = false; 266 m_testCtx.getLog().endCase(testResult, testResultDesc); 267 268 // Update statistics. 269 print(" %s (%s)\n", qpGetTestResultName(testResult), testResultDesc); 270 271 m_status.numExecuted += 1; 272 switch (testResult) 273 { 274 case QP_TEST_RESULT_PASS: m_status.numPassed += 1; break; 275 case QP_TEST_RESULT_NOT_SUPPORTED: m_status.numNotSupported += 1; break; 276 case QP_TEST_RESULT_QUALITY_WARNING: m_status.numWarnings += 1; break; 277 case QP_TEST_RESULT_COMPATIBILITY_WARNING: m_status.numWarnings += 1; break; 278 default: m_status.numFailed += 1; break; 279 } 280 281 // terminateAfter, Resource error or any error in deinit means that execution should end 282 if (terminateAfter || testResult == QP_TEST_RESULT_RESOURCE_ERROR) 283 m_abortSession = true; 284 } 285 286 if (m_testCtx.getWatchDog()) 287 qpWatchDog_reset(m_testCtx.getWatchDog()); 288 } 289 290 TestCase::IterateResult TestSessionExecutor::iterateTestCase (TestCase* testCase) 291 { 292 TestLog& log = m_testCtx.getLog(); 293 TestCase::IterateResult iterateResult = TestCase::STOP; 294 295 m_testCtx.touchWatchdog(); 296 297 try 298 { 299 iterateResult = m_caseExecutor->iterate(testCase); 300 } 301 catch (const std::bad_alloc&) 302 { 303 m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test execution"); 304 m_testCtx.setTerminateAfter(true); 305 } 306 catch (const tcu::TestException& e) 307 { 308 log << e; 309 m_testCtx.setTestResult(e.getTestResult(), e.getMessage()); 310 m_testCtx.setTerminateAfter(e.isFatal()); 311 } 312 catch (const tcu::Exception& e) 313 { 314 log << e; 315 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage()); 316 } 317 318 return iterateResult; 319 } 320 321 } // tcu 322