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 Render target info. 22 *//*--------------------------------------------------------------------*/ 23 24#include "tcuApp.hpp" 25#include "tcuPlatform.hpp" 26#include "tcuTestContext.hpp" 27#include "tcuTestSessionExecutor.hpp" 28#include "tcuTestHierarchyUtil.hpp" 29#include "tcuCommandLine.hpp" 30#include "tcuTestLog.hpp" 31 32#include "qpInfo.h" 33#include "qpDebugOut.h" 34 35#include "deMath.h" 36 37#include <iostream> 38 39namespace tcu 40{ 41 42using std::string; 43 44/*--------------------------------------------------------------------*//*! 45 * Writes all packages found stdout without any 46 * separations. Recommended to be used with a single package 47 * only. It's possible to use test selectors for limiting the export 48 * to one package in a multipackage binary. 49 *//*--------------------------------------------------------------------*/ 50static void writeCaselistsToStdout (TestPackageRoot& root, TestContext& testCtx) 51{ 52 DefaultHierarchyInflater inflater (testCtx); 53 de::MovePtr<const CaseListFilter> caseListFilter (testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive())); 54 TestHierarchyIterator iter (root, inflater, *caseListFilter); 55 56 while (iter.getState() != TestHierarchyIterator::STATE_FINISHED) 57 { 58 iter.next(); 59 60 while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE) 61 { 62 if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE) 63 std::cout << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n"; 64 iter.next(); 65 } 66 67 DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE && 68 iter.getNode()->getNodeType() == NODETYPE_PACKAGE); 69 iter.next(); 70 } 71} 72 73 74/*--------------------------------------------------------------------*//*! 75 * Verifies that amber capability requirements in the .amber files 76 * match with capabilities defined on the CTS C code. 77 *//*--------------------------------------------------------------------*/ 78static void verifyAmberCapabilityCoherency (TestPackageRoot& root, TestContext& testCtx) 79{ 80 DefaultHierarchyInflater inflater(testCtx); 81 de::MovePtr<const CaseListFilter> caseListFilter(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive())); 82 TestHierarchyIterator iter(root, inflater, *caseListFilter); 83 int count = 0; 84 int errorCount = 0; 85 86 bool ok = true; 87 88 while (iter.getState() != TestHierarchyIterator::STATE_FINISHED) 89 { 90 iter.next(); 91 92 while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE) 93 { 94 if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE && 95 isTestNodeTypeExecutable(iter.getNode()->getNodeType())) 96 { 97 std::cout << iter.getNodePath() << "\n"; 98 testCtx.getLog() << tcu::TestLog::Message << iter.getNodePath() << tcu::TestLog::EndMessage; 99 if (!iter.getNode()->validateRequirements()) 100 { 101 ok = false; 102 errorCount++; 103 } 104 count++; 105 } 106 iter.next(); 107 } 108 109 DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE && 110 iter.getNode()->getNodeType() == NODETYPE_PACKAGE); 111 iter.next(); 112 } 113 std::cout << count << " amber tests, " << errorCount << " errors.\n"; 114 if (!ok) 115 TCU_THROW(InternalError, "One or more CTS and Amber test requirements do not match; check log for details"); 116 117} 118 119/*--------------------------------------------------------------------*//*! 120 * \brief Construct test application 121 * 122 * If a fatal error occurs during initialization constructor will call 123 * die() with debug information. 124 * 125 * \param platform Reference to platform implementation. 126 *//*--------------------------------------------------------------------*/ 127App::App (Platform& platform, Archive& archive, TestLog& log, const CommandLine& cmdLine) 128 : m_platform (platform) 129 , m_watchDog (DE_NULL) 130 , m_crashHandler (DE_NULL) 131 , m_crashed (false) 132 , m_testCtx (DE_NULL) 133 , m_testRoot (DE_NULL) 134 , m_testExecutor (DE_NULL) 135{ 136 if (!cmdLine.isSubProcess()) 137 { 138 print("dEQP Core %s (0x%08x) starting..\n", qpGetReleaseName(), qpGetReleaseId()); 139 print(" target implementation = '%s'\n", qpGetTargetName()); 140 } 141 142 if (!deSetRoundingMode(DE_ROUNDINGMODE_TO_NEAREST_EVEN)) 143 qpPrintf("WARNING: Failed to set floating-point rounding mode!\n"); 144 145 try 146 { 147 const RunMode runMode = cmdLine.getRunMode(); 148 149 // Initialize watchdog 150 if (cmdLine.isWatchDogEnabled()) 151 TCU_CHECK_INTERNAL(m_watchDog = qpWatchDog_create(onWatchdogTimeout, this, WATCHDOG_TOTAL_TIME_LIMIT_SECS, WATCHDOG_INTERVAL_TIME_LIMIT_SECS)); 152 153 // Initialize crash handler. 154 if (cmdLine.isCrashHandlingEnabled()) 155 TCU_CHECK_INTERNAL(m_crashHandler = qpCrashHandler_create(onCrash, this)); 156 157 // Create test context 158 m_testCtx = new TestContext(m_platform, archive, log, cmdLine, m_watchDog); 159 160 // Create root from registry 161 m_testRoot = new TestPackageRoot(*m_testCtx, TestPackageRegistry::getSingleton()); 162 163 // \note No executor is created if runmode is not EXECUTE 164 if (runMode == RUNMODE_EXECUTE) 165 m_testExecutor = new TestSessionExecutor(*m_testRoot, *m_testCtx); 166 else if (runMode == RUNMODE_DUMP_STDOUT_CASELIST) 167 writeCaselistsToStdout(*m_testRoot, *m_testCtx); 168 else if (runMode == RUNMODE_DUMP_XML_CASELIST) 169 writeXmlCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine); 170 else if (runMode == RUNMODE_DUMP_TEXT_CASELIST) 171 writeTxtCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine); 172 else if (runMode == RUNMODE_VERIFY_AMBER_COHERENCY) 173 verifyAmberCapabilityCoherency(*m_testRoot, *m_testCtx); 174 else 175 DE_ASSERT(false); 176 } 177 catch (const std::exception& e) 178 { 179 cleanup(); 180 die("Failed to initialize dEQP: %s", e.what()); 181 } 182} 183 184App::~App (void) 185{ 186 cleanup(); 187} 188 189void App::cleanup (void) 190{ 191 delete m_testExecutor; 192 delete m_testRoot; 193 delete m_testCtx; 194 195 if (m_crashHandler) 196 qpCrashHandler_destroy(m_crashHandler); 197 198 if (m_watchDog) 199 qpWatchDog_destroy(m_watchDog); 200} 201 202/*--------------------------------------------------------------------*//*! 203 * \brief Step forward test execution 204 * \return true if application should call iterate() again and false 205 * if test execution session is complete. 206 *//*--------------------------------------------------------------------*/ 207bool App::iterate (void) 208{ 209 if (!m_testExecutor) 210 { 211 DE_ASSERT(m_testCtx->getCommandLine().getRunMode() != RUNMODE_EXECUTE); 212 return false; 213 } 214 215 // Poll platform events 216 const bool platformOk = m_platform.processEvents(); 217 218 // Iterate a step. 219 bool testExecOk = false; 220 if (platformOk) 221 { 222 try 223 { 224 testExecOk = m_testExecutor->iterate(); 225 } 226 catch (const std::exception& e) 227 { 228 die("%s", e.what()); 229 } 230 } 231 232 if ((!platformOk || !testExecOk) ) 233 { 234 if (!m_testCtx->getCommandLine().isSubProcess()) 235 { 236 if (!platformOk) 237 print("\nABORTED!\n"); 238 else 239 print("\nDONE!\n"); 240 } 241 242 const RunMode runMode = m_testCtx->getCommandLine().getRunMode(); 243 if (runMode == RUNMODE_EXECUTE) 244 { 245 const TestRunStatus& result = m_testExecutor->getStatus(); 246 if(!m_testCtx->getCommandLine().isSubProcess()) 247 { 248 // Report statistics. 249 print("\nTest run totals:\n"); 250 print(" Passed: %d/%d (%.1f%%)\n", result.numPassed, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numPassed / (float)result.numExecuted) : 0.0f)); 251 print(" Failed: %d/%d (%.1f%%)\n", result.numFailed, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numFailed / (float)result.numExecuted) : 0.0f)); 252 print(" Not supported: %d/%d (%.1f%%)\n", result.numNotSupported, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numNotSupported / (float)result.numExecuted) : 0.0f)); 253 print(" Warnings: %d/%d (%.1f%%)\n", result.numWarnings, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWarnings / (float)result.numExecuted) : 0.0f)); 254 print(" Waived: %d/%d (%.1f%%)\n", result.numWaived, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWaived / (float)result.numExecuted) : 0.0f)); 255 if (!result.isComplete) 256 print("Test run was ABORTED!\n"); 257 } 258 else 259 { 260 // subprocess sends test statisticts through qpa file, so that main process may read it 261 // and add to global statistics ( search for #SubProcessStatus to see how it's done ) 262 std::ostringstream str; 263 str << "\n#SubProcessStatus " << 264 result.numExecuted << " " << 265 result.numPassed << " " << 266 result.numFailed << " " << 267 result.numNotSupported << " " << 268 result.numWarnings << " " << 269 result.numWaived << "\n"; 270 m_testCtx->getLog().writeRaw(str.str().c_str()); 271 } 272 } 273 } 274 275 return platformOk && testExecOk; 276} 277 278const TestRunStatus& App::getResult (void) const 279{ 280 return m_testExecutor->getStatus(); 281} 282 283void App::onWatchdogTimeout (qpWatchDog* watchDog, void* userPtr, qpTimeoutReason reason) 284{ 285 DE_UNREF(watchDog); 286 static_cast<App*>(userPtr)->onWatchdogTimeout(reason); 287} 288 289void App::onCrash (qpCrashHandler* crashHandler, void* userPtr) 290{ 291 DE_UNREF(crashHandler); 292 static_cast<App*>(userPtr)->onCrash(); 293} 294 295void App::onWatchdogTimeout (qpTimeoutReason reason) 296{ 297 if (!m_crashLock.tryLock() || m_crashed) 298 return; // In crash handler already. 299 300 m_crashed = true; 301 302 m_testCtx->getLog().terminateCase(QP_TEST_RESULT_TIMEOUT); 303 die("Watchdog timer timeout for %s", (reason == QP_TIMEOUT_REASON_INTERVAL_LIMIT ? "touch interval" : "total time")); 304} 305 306static void writeCrashToLog (void* userPtr, const char* infoString) 307{ 308 // \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED! 309 TestLog* log = static_cast<TestLog*>(userPtr); 310 log->writeMessage(infoString); 311} 312 313static void writeCrashToConsole (void* userPtr, const char* infoString) 314{ 315 // \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED! 316 DE_UNREF(userPtr); 317 qpPrint(infoString); 318} 319 320void App::onCrash (void) 321{ 322 // \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED! 323 324 if (!m_crashLock.tryLock() || m_crashed) 325 return; // In crash handler already. 326 327 m_crashed = true; 328 329 bool isInCase = m_testExecutor ? m_testExecutor->isInTestCase() : false; 330 331 if (isInCase) 332 { 333 qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToLog, &m_testCtx->getLog()); 334 m_testCtx->getLog().terminateCase(QP_TEST_RESULT_CRASH); 335 } 336 else 337 qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToConsole, DE_NULL); 338 339 die("Test program crashed"); 340} 341 342} // tcu 343