| /*------------------------------------------------------------------------- | |
| * 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 "deClock.h" | |
| #include "deStringUtil.hpp" | |
| #include "tcuCommandLine.hpp" | |
| #include "tcuTestLog.hpp" | |
| #include <algorithm> | |
| #include <cstdio> | |
| #include <fstream> | |
| using std::string; | |
| using std::vector; | |
| 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<TestCaseGroup *>(curEntry.node)); | |
| else if (curEntry.node->getNodeType() == NODETYPE_PACKAGE) | |
| m_inflater.leaveTestPackage(static_cast<TestPackage *>(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<TestNode *> &nodes, const std::string &name) | |
| { | |
| for (vector<TestNode *>::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<string> 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<TestPackage *>(curNode); | |
| m_inflater.enterTestPackage(testPackage, m_nodeStack.back().children); | |
| DE_ASSERT(!m_caseExecutor); | |
| m_caseExecutor = de::MovePtr<TestCaseExecutor>(testPackage->createExecutor()); | |
| } | |
| else if (curNode->getNodeType() == NODETYPE_GROUP) | |
| m_inflater.enterGroupNode(static_cast<TestCaseGroup *>(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<TestCase *>(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; | |
| } | |
| } | |
| } // namespace tcu |