/*------------------------------------------------------------------------- * 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 Executor which can run randomly accessed tests. *//*--------------------------------------------------------------------*/ #include "tcuRandomOrderExecutor.h" #include "tcuCommandLine.hpp" #include "tcuTestLog.hpp" #include "deStringUtil.hpp" #include "deClock.h" #include #include #include using std::vector; using std::string; namespace tcu { RandomOrderExecutor::RandomOrderExecutor(TestPackageRoot &root, TestContext &testCtx) : m_testCtx(testCtx), m_inflater(testCtx) { m_nodeStack.push_back(NodeStackEntry(&root)); root.getChildren(m_nodeStack[0].children); } RandomOrderExecutor::~RandomOrderExecutor(void) { pruneStack(1); } void RandomOrderExecutor::pruneStack(size_t newStackSize) { // \note Root is not managed by us DE_ASSERT(de::inRange(newStackSize, size_t(1), m_nodeStack.size())); while (m_nodeStack.size() > newStackSize) { NodeStackEntry &curEntry = m_nodeStack.back(); const bool isPkg = curEntry.node->getNodeType() == NODETYPE_PACKAGE; DE_ASSERT((m_nodeStack.size() == 2) == isPkg); if (curEntry.node) // Just in case we are in // cleanup path after partial // prune { if (curEntry.node->getNodeType() == NODETYPE_GROUP) m_inflater.leaveGroupNode(static_cast(curEntry.node)); else if (curEntry.node->getNodeType() == NODETYPE_PACKAGE) m_inflater.leaveTestPackage(static_cast(curEntry.node)); else DE_ASSERT(curEntry.children.empty()); curEntry.node = DE_NULL; curEntry.children.clear(); } if (isPkg) m_caseExecutor.clear(); m_nodeStack.pop_back(); } } static TestNode *findNodeByName(vector &nodes, const std::string &name) { for (vector::const_iterator node = nodes.begin(); node != nodes.end(); ++node) { if (name == (*node)->getName()) return *node; } return DE_NULL; } TestCase *RandomOrderExecutor::seekToCase(const string &path) { const vector components = de::splitString(path, '.'); size_t compNdx; DE_ASSERT(!m_nodeStack.empty() && m_nodeStack.front().node->getNodeType() == NODETYPE_ROOT); for (compNdx = 0; compNdx < components.size(); compNdx++) { const size_t stackPos = compNdx + 1; if (stackPos >= m_nodeStack.size()) break; // Stack end else if (components[compNdx] != m_nodeStack[stackPos].node->getName()) { // Current stack doesn't match any more, prune. pruneStack(stackPos); break; } } DE_ASSERT(m_nodeStack.size() == compNdx + 1); for (; compNdx < components.size(); compNdx++) { const size_t parentStackPos = compNdx; TestNode *const curNode = findNodeByName(m_nodeStack[parentStackPos].children, components[compNdx]); if (!curNode) throw Exception(string("Test hierarchy node not found: ") + path); m_nodeStack.push_back(NodeStackEntry(curNode)); if (curNode->getNodeType() == NODETYPE_PACKAGE) { TestPackage *const testPackage = static_cast(curNode); m_inflater.enterTestPackage(testPackage, m_nodeStack.back().children); DE_ASSERT(!m_caseExecutor); m_caseExecutor = de::MovePtr(testPackage->createExecutor()); } else if (curNode->getNodeType() == NODETYPE_GROUP) m_inflater.enterGroupNode(static_cast(curNode), m_nodeStack.back().children); else if (compNdx + 1 != components.size()) throw Exception(string("Invalid test hierarchy path: ") + path); } DE_ASSERT(m_nodeStack.size() == components.size() + 1); if (isTestNodeTypeExecutable(m_nodeStack.back().node->getNodeType())) return dynamic_cast(m_nodeStack.back().node); else throw Exception(string("Not a test case: ") + path); } static qpTestCaseType nodeTypeToTestCaseType(TestNodeType nodeType) { switch (nodeType) { case NODETYPE_SELF_VALIDATE: return QP_TEST_CASE_TYPE_SELF_VALIDATE; case NODETYPE_PERFORMANCE: return QP_TEST_CASE_TYPE_PERFORMANCE; case NODETYPE_CAPABILITY: return QP_TEST_CASE_TYPE_CAPABILITY; case NODETYPE_ACCURACY: return QP_TEST_CASE_TYPE_ACCURACY; default: DE_ASSERT(false); return QP_TEST_CASE_TYPE_LAST; } } TestStatus RandomOrderExecutor::execute(const std::string &casePath) { TestCase *const testCase = seekToCase(casePath); TestLog &log = m_testCtx.getLog(); const qpTestCaseType caseType = nodeTypeToTestCaseType(testCase->getNodeType()); m_testCtx.setTerminateAfter(false); log.startCase(casePath.c_str(), caseType); { const TestStatus result = executeInner(testCase, casePath); log.endCase(result.getCode(), result.getDescription().c_str()); return result; } } tcu::TestStatus RandomOrderExecutor::executeInner(TestCase *testCase, const std::string &casePath) { TestLog &log = m_testCtx.getLog(); const deUint64 testStartTime = deGetMicroseconds(); m_testCtx.setTestResult(QP_TEST_RESULT_LAST, ""); // Initialize, will return immediately if fails try { m_caseExecutor->init(testCase, casePath); } catch (const std::bad_alloc &) { m_testCtx.setTerminateAfter(true); return TestStatus(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory in test case init"); } catch (const TestException &e) { DE_ASSERT(e.getTestResult() != QP_TEST_RESULT_LAST); m_testCtx.setTerminateAfter(e.isFatal()); log << e; return TestStatus(e.getTestResult(), e.getMessage()); } catch (const Exception &e) { log << e; return TestStatus(QP_TEST_RESULT_FAIL, e.getMessage()); } // Execute for (;;) { TestCase::IterateResult iterateResult = TestCase::STOP; m_testCtx.touchWatchdog(); try { iterateResult = m_caseExecutor->iterate(testCase); } catch (const std::bad_alloc &) { m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Failed to allocate memory during test " "execution"); } catch (const TestException &e) { log << e; m_testCtx.setTestResult(e.getTestResult(), e.getMessage()); m_testCtx.setTerminateAfter(e.isFatal()); } catch (const Exception &e) { log << e; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, e.getMessage()); } if (iterateResult == TestCase::STOP) break; } DE_ASSERT(m_testCtx.getTestResult() != QP_TEST_RESULT_LAST); if (m_testCtx.getTestResult() == QP_TEST_RESULT_RESOURCE_ERROR) m_testCtx.setTerminateAfter(true); // De-initialize try { m_caseExecutor->deinit(testCase); } catch (const tcu::Exception &e) { log << e << TestLog::Message << "Error in test case deinit, test program " "will terminate." << TestLog::EndMessage; m_testCtx.setTerminateAfter(true); } if (m_testCtx.getWatchDog()) qpWatchDog_reset(m_testCtx.getWatchDog()); { const TestStatus result = TestStatus(m_testCtx.getTestResult(), m_testCtx.getTestResultDesc()); m_testCtx.setTestResult(QP_TEST_RESULT_LAST, ""); return result; } } } // tcu