/*------------------------------------------------------------------------- * drawElements Quality Program Tester Core * ---------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Render target info. *//*--------------------------------------------------------------------*/ #include "tcuApp.hpp" #include "tcuPlatform.hpp" #include "tcuTestContext.hpp" #include "tcuTestSessionExecutor.hpp" #include "tcuTestHierarchyUtil.hpp" #include "tcuCommandLine.hpp" #include "tcuTestLog.hpp" #include "qpInfo.h" #include "qpDebugOut.h" #include "deMath.h" #include namespace tcu { using std::string; /*--------------------------------------------------------------------*//*! * Writes all packages found stdout without any * separations. Recommended to be used with a single package * only. It's possible to use test selectors for limiting the export * to one package in a multipackage binary. *//*--------------------------------------------------------------------*/ static void writeCaselistsToStdout (TestPackageRoot& root, TestContext& testCtx) { DefaultHierarchyInflater inflater (testCtx); de::MovePtr caseListFilter (testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive())); TestHierarchyIterator iter (root, inflater, *caseListFilter); while (iter.getState() != TestHierarchyIterator::STATE_FINISHED) { iter.next(); while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE) { if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE) std::cout << (isTestNodeTypeExecutable(iter.getNode()->getNodeType()) ? "TEST" : "GROUP") << ": " << iter.getNodePath() << "\n"; iter.next(); } DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE && iter.getNode()->getNodeType() == NODETYPE_PACKAGE); iter.next(); } } /*--------------------------------------------------------------------*//*! * \brief Construct test application * * If a fatal error occurs during initialization constructor will call * die() with debug information. * * \param platform Reference to platform implementation. *//*--------------------------------------------------------------------*/ App::App (Platform& platform, Archive& archive, TestLog& log, const CommandLine& cmdLine) : m_platform (platform) , m_watchDog (DE_NULL) , m_crashHandler (DE_NULL) , m_crashed (false) , m_testCtx (DE_NULL) , m_testRoot (DE_NULL) , m_testExecutor (DE_NULL) { print("dEQP Core %s (0x%08x) starting..\n", qpGetReleaseName(), qpGetReleaseId()); print(" target implementation = '%s'\n", qpGetTargetName()); if (!deSetRoundingMode(DE_ROUNDINGMODE_TO_NEAREST_EVEN)) qpPrintf("WARNING: Failed to set floating-point rounding mode!\n"); try { const RunMode runMode = cmdLine.getRunMode(); // Initialize watchdog if (cmdLine.isWatchDogEnabled()) TCU_CHECK_INTERNAL(m_watchDog = qpWatchDog_create(onWatchdogTimeout, this, WATCHDOG_TOTAL_TIME_LIMIT_SECS, WATCHDOG_INTERVAL_TIME_LIMIT_SECS)); // Initialize crash handler. if (cmdLine.isCrashHandlingEnabled()) TCU_CHECK_INTERNAL(m_crashHandler = qpCrashHandler_create(onCrash, this)); // Create test context m_testCtx = new TestContext(m_platform, archive, log, cmdLine, m_watchDog); // Create root from registry m_testRoot = new TestPackageRoot(*m_testCtx, TestPackageRegistry::getSingleton()); // \note No executor is created if runmode is not EXECUTE if (runMode == RUNMODE_EXECUTE) m_testExecutor = new TestSessionExecutor(*m_testRoot, *m_testCtx); else if (runMode == RUNMODE_DUMP_STDOUT_CASELIST) writeCaselistsToStdout(*m_testRoot, *m_testCtx); else if (runMode == RUNMODE_DUMP_XML_CASELIST) writeXmlCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine); else if (runMode == RUNMODE_DUMP_TEXT_CASELIST) writeTxtCaselistsToFiles(*m_testRoot, *m_testCtx, cmdLine); else DE_ASSERT(false); } catch (const std::exception& e) { cleanup(); die("Failed to initialize dEQP: %s", e.what()); } } App::~App (void) { cleanup(); } void App::cleanup (void) { delete m_testExecutor; delete m_testRoot; delete m_testCtx; if (m_crashHandler) qpCrashHandler_destroy(m_crashHandler); if (m_watchDog) qpWatchDog_destroy(m_watchDog); } /*--------------------------------------------------------------------*//*! * \brief Step forward test execution * \return true if application should call iterate() again and false * if test execution session is complete. *//*--------------------------------------------------------------------*/ bool App::iterate (void) { if (!m_testExecutor) { DE_ASSERT(m_testCtx->getCommandLine().getRunMode() != RUNMODE_EXECUTE); return false; } // Poll platform events const bool platformOk = m_platform.processEvents(); // Iterate a step. bool testExecOk = false; if (platformOk) { try { testExecOk = m_testExecutor->iterate(); } catch (const std::exception& e) { die("%s", e.what()); } } if (!platformOk || !testExecOk) { if (!platformOk) print("\nABORTED!\n"); else print("\nDONE!\n"); const RunMode runMode = m_testCtx->getCommandLine().getRunMode(); if (runMode == RUNMODE_EXECUTE) { const TestRunStatus& result = m_testExecutor->getStatus(); // Report statistics. print("\nTest run totals:\n"); print(" Passed: %d/%d (%.1f%%)\n", result.numPassed, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numPassed / (float)result.numExecuted) : 0.0f)); print(" Failed: %d/%d (%.1f%%)\n", result.numFailed, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numFailed / (float)result.numExecuted) : 0.0f)); print(" Not supported: %d/%d (%.1f%%)\n", result.numNotSupported, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numNotSupported / (float)result.numExecuted) : 0.0f)); print(" Warnings: %d/%d (%.1f%%)\n", result.numWarnings, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWarnings / (float)result.numExecuted) : 0.0f)); if (!result.isComplete) print("Test run was ABORTED!\n"); } } return platformOk && testExecOk; } const TestRunStatus& App::getResult (void) const { return m_testExecutor->getStatus(); } void App::onWatchdogTimeout (qpWatchDog* watchDog, void* userPtr, qpTimeoutReason reason) { DE_UNREF(watchDog); static_cast(userPtr)->onWatchdogTimeout(reason); } void App::onCrash (qpCrashHandler* crashHandler, void* userPtr) { DE_UNREF(crashHandler); static_cast(userPtr)->onCrash(); } void App::onWatchdogTimeout (qpTimeoutReason reason) { if (!m_crashLock.tryLock() || m_crashed) return; // In crash handler already. m_crashed = true; m_testCtx->getLog().terminateCase(QP_TEST_RESULT_TIMEOUT); die("Watchdog timer timeout for %s", (reason == QP_TIMEOUT_REASON_INTERVAL_LIMIT ? "touch interval" : "total time")); } static void writeCrashToLog (void* userPtr, const char* infoString) { // \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED! TestLog* log = static_cast(userPtr); log->writeMessage(infoString); } static void writeCrashToConsole (void* userPtr, const char* infoString) { // \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED! DE_UNREF(userPtr); qpPrint(infoString); } void App::onCrash (void) { // \note THIS IS CALLED BY SIGNAL HANDLER! CALLING MALLOC/FREE IS NOT ALLOWED! if (!m_crashLock.tryLock() || m_crashed) return; // In crash handler already. m_crashed = true; bool isInCase = m_testExecutor ? m_testExecutor->isInTestCase() : false; if (isInCase) { qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToLog, &m_testCtx->getLog()); m_testCtx->getLog().terminateCase(QP_TEST_RESULT_CRASH); } else qpCrashHandler_writeCrashInfo(m_crashHandler, writeCrashToConsole, DE_NULL); die("Test program crashed"); } } // tcu