| /*------------------------------------------------------------------------- |
| * 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 <iostream> |
| |
| 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<const CaseListFilter> 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(); |
| } |
| } |
| |
| |
| /*--------------------------------------------------------------------*//*! |
| * Verifies that amber capability requirements in the .amber files |
| * match with capabilities defined on the CTS C code. |
| *//*--------------------------------------------------------------------*/ |
| static void verifyAmberCapabilityCoherency (TestPackageRoot& root, TestContext& testCtx) |
| { |
| DefaultHierarchyInflater inflater(testCtx); |
| de::MovePtr<const CaseListFilter> caseListFilter(testCtx.getCommandLine().createCaseListFilter(testCtx.getArchive())); |
| TestHierarchyIterator iter(root, inflater, *caseListFilter); |
| int count = 0; |
| int errorCount = 0; |
| |
| bool ok = true; |
| |
| while (iter.getState() != TestHierarchyIterator::STATE_FINISHED) |
| { |
| iter.next(); |
| |
| while (iter.getNode()->getNodeType() != NODETYPE_PACKAGE) |
| { |
| if (iter.getState() == TestHierarchyIterator::STATE_ENTER_NODE && |
| isTestNodeTypeExecutable(iter.getNode()->getNodeType())) |
| { |
| std::cout << iter.getNodePath() << "\n"; |
| testCtx.getLog() << tcu::TestLog::Message << iter.getNodePath() << tcu::TestLog::EndMessage; |
| if (!iter.getNode()->validateRequirements()) |
| { |
| ok = false; |
| errorCount++; |
| } |
| count++; |
| } |
| iter.next(); |
| } |
| |
| DE_ASSERT(iter.getState() == TestHierarchyIterator::STATE_LEAVE_NODE && |
| iter.getNode()->getNodeType() == NODETYPE_PACKAGE); |
| iter.next(); |
| } |
| std::cout << count << " amber tests, " << errorCount << " errors.\n"; |
| if (!ok) |
| TCU_THROW(InternalError, "One or more CTS and Amber test requirements do not match; check log for details"); |
| |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \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 if (runMode == RUNMODE_VERIFY_AMBER_COHERENCY) |
| verifyAmberCapabilityCoherency(*m_testRoot, *m_testCtx); |
| 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)); |
| print(" Waived: %d/%d (%.1f%%)\n", result.numWaived, result.numExecuted, (result.numExecuted > 0 ? (100.0f * (float)result.numWaived / (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<App*>(userPtr)->onWatchdogTimeout(reason); |
| } |
| |
| void App::onCrash (qpCrashHandler* crashHandler, void* userPtr) |
| { |
| DE_UNREF(crashHandler); |
| static_cast<App*>(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<TestLog*>(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 |