| /*------------------------------------------------------------------------- |
| * 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 Test case hierarchy iterator. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "tcuTestHierarchyIterator.hpp" |
| #include "tcuCommandLine.hpp" |
| |
| namespace tcu |
| { |
| |
| using std::string; |
| using std::vector; |
| |
| // TestHierarchyInflater |
| |
| TestHierarchyInflater::TestHierarchyInflater(void) |
| { |
| } |
| |
| TestHierarchyInflater::~TestHierarchyInflater(void) |
| { |
| } |
| |
| // DefaultHierarchyInflater |
| |
| DefaultHierarchyInflater::DefaultHierarchyInflater(TestContext &testCtx) : m_testCtx(testCtx) |
| { |
| } |
| |
| DefaultHierarchyInflater::~DefaultHierarchyInflater(void) |
| { |
| } |
| |
| void DefaultHierarchyInflater::enterTestPackage(TestPackage *testPackage, vector<TestNode *> &children) |
| { |
| { |
| Archive *const pkgArchive = testPackage->getArchive(); |
| |
| if (pkgArchive) |
| m_testCtx.setCurrentArchive(*pkgArchive); |
| else |
| m_testCtx.setCurrentArchive(m_testCtx.getRootArchive()); |
| } |
| |
| testPackage->init(); |
| testPackage->getChildren(children); |
| |
| // write default session info if it was not done by package |
| m_testCtx.writeSessionInfo(); |
| } |
| |
| void DefaultHierarchyInflater::leaveTestPackage(TestPackage *testPackage) |
| { |
| m_testCtx.setCurrentArchive(m_testCtx.getRootArchive()); |
| testPackage->deinit(); |
| } |
| |
| void DefaultHierarchyInflater::enterGroupNode(TestCaseGroup *testGroup, vector<TestNode *> &children) |
| { |
| testGroup->init(); |
| testGroup->getChildren(children); |
| } |
| |
| void DefaultHierarchyInflater::leaveGroupNode(TestCaseGroup *testGroup) |
| { |
| testGroup->deinit(); |
| } |
| |
| // TestHierarchyIterator |
| |
| TestHierarchyIterator::TestHierarchyIterator(TestPackageRoot &rootNode, TestHierarchyInflater &inflater, |
| const CaseListFilter &caseListFilter) |
| : m_inflater(inflater) |
| , m_caseListFilter(caseListFilter) |
| , m_groupNumber(0) |
| { |
| // Init traverse state and "seek" to first reportable node. |
| NodeIter iter(&rootNode); |
| iter.setState(NodeIter::NISTATE_ENTER); // Root is never reported |
| m_sessionStack.push_back(iter); |
| next(); |
| } |
| |
| TestHierarchyIterator::~TestHierarchyIterator(void) |
| { |
| // Tear down inflated nodes in m_sessionStack |
| for (vector<NodeIter>::reverse_iterator iter = m_sessionStack.rbegin(); iter != m_sessionStack.rend(); ++iter) |
| { |
| TestNode *const node = iter->node; |
| const TestNodeType nodeType = node->getNodeType(); |
| |
| switch (nodeType) |
| { |
| case NODETYPE_ROOT: /* root is not de-initialized */ |
| break; |
| case NODETYPE_PACKAGE: |
| m_inflater.leaveTestPackage(static_cast<TestPackage *>(node)); |
| break; |
| case NODETYPE_GROUP: |
| m_inflater.leaveGroupNode(static_cast<TestCaseGroup *>(node)); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| TestHierarchyIterator::State TestHierarchyIterator::getState(void) const |
| { |
| if (!m_sessionStack.empty()) |
| { |
| const NodeIter &iter = m_sessionStack.back(); |
| |
| DE_ASSERT(iter.getState() == NodeIter::NISTATE_ENTER || iter.getState() == NodeIter::NISTATE_LEAVE); |
| |
| return iter.getState() == NodeIter::NISTATE_ENTER ? STATE_ENTER_NODE : STATE_LEAVE_NODE; |
| } |
| else |
| return STATE_FINISHED; |
| } |
| |
| TestNode *TestHierarchyIterator::getNode(void) const |
| { |
| DE_ASSERT(getState() != STATE_FINISHED); |
| return m_sessionStack.back().node; |
| } |
| |
| const std::string &TestHierarchyIterator::getNodePath(void) const |
| { |
| DE_ASSERT(getState() != STATE_FINISHED); |
| return m_nodePath; |
| } |
| |
| std::string TestHierarchyIterator::buildNodePath(const vector<NodeIter> &nodeStack) |
| { |
| string nodePath; |
| for (size_t ndx = 1; ndx < nodeStack.size(); ndx++) |
| { |
| const NodeIter &iter = nodeStack[ndx]; |
| if (ndx > 1) // ignore root package |
| nodePath += "."; |
| nodePath += iter.node->getName(); |
| } |
| return nodePath; |
| } |
| |
| void TestHierarchyIterator::next(void) |
| { |
| while (!m_sessionStack.empty()) |
| { |
| NodeIter &iter = m_sessionStack.back(); |
| TestNode *const node = iter.node; |
| const bool isLeaf = isTestNodeTypeExecutable(node->getNodeType()); |
| |
| switch (iter.getState()) |
| { |
| case NodeIter::NISTATE_INIT: |
| { |
| const std::string nodePath = buildNodePath(m_sessionStack); |
| |
| // Return to parent if name or runner type doesn't match filter. |
| if (!(isLeaf ? (m_caseListFilter.checkRunnerType(node->getRunnerType()) && |
| m_caseListFilter.checkTestCaseName(nodePath.c_str())) : |
| m_caseListFilter.checkTestGroupName(nodePath.c_str()))) |
| { |
| m_sessionStack.pop_back(); |
| break; |
| } |
| |
| m_nodePath = nodePath; |
| iter.setState(NodeIter::NISTATE_ENTER); |
| return; // Yield enter event |
| } |
| |
| case NodeIter::NISTATE_ENTER: |
| { |
| if (isLeaf) |
| { |
| iter.setState(NodeIter::NISTATE_LEAVE); |
| return; // Yield leave event |
| } |
| else |
| { |
| iter.setState(NodeIter::NISTATE_TRAVERSE_CHILDREN); |
| iter.children.clear(); |
| |
| switch (node->getNodeType()) |
| { |
| case NODETYPE_ROOT: |
| static_cast<TestPackageRoot *>(node)->getChildren(iter.children); |
| break; |
| case NODETYPE_PACKAGE: |
| m_inflater.enterTestPackage(static_cast<TestPackage *>(node), iter.children); |
| break; |
| case NODETYPE_GROUP: |
| m_inflater.enterGroupNode(static_cast<TestCaseGroup *>(node), iter.children); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| |
| break; |
| } |
| |
| case NodeIter::NISTATE_TRAVERSE_CHILDREN: |
| { |
| int numChildren = (int)iter.children.size(); |
| if (++iter.curChildNdx < numChildren) |
| { |
| // Push child to stack. |
| TestNode *childNode = iter.children[iter.curChildNdx]; |
| |
| // Check whether this is a bottom-level group (child is executable) |
| // and whether that group should be filtered out. |
| if (isTestNodeTypeExecutable(childNode->getNodeType())) |
| { |
| const std::string testName = m_nodePath + "." + childNode->getName(); |
| if (!m_caseListFilter.checkCaseFraction(m_groupNumber, testName)) |
| break; |
| } |
| m_sessionStack.push_back(NodeIter(childNode)); |
| } |
| else |
| { |
| iter.setState(NodeIter::NISTATE_LEAVE); |
| if (node->getNodeType() != NODETYPE_ROOT) |
| return; // Yield leave event |
| } |
| |
| break; |
| } |
| |
| case NodeIter::NISTATE_LEAVE: |
| { |
| // Leave node. |
| if (!isLeaf) |
| { |
| switch (node->getNodeType()) |
| { |
| case NODETYPE_ROOT: /* root is not de-initialized */ |
| break; |
| case NODETYPE_PACKAGE: |
| m_inflater.leaveTestPackage(static_cast<TestPackage *>(node)); |
| break; |
| case NODETYPE_GROUP: |
| m_inflater.leaveGroupNode(static_cast<TestCaseGroup *>(node)); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| m_groupNumber++; |
| } |
| |
| m_sessionStack.pop_back(); |
| m_nodePath = buildNodePath(m_sessionStack); |
| break; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| return; |
| } |
| } |
| |
| DE_ASSERT(m_sessionStack.empty() && getState() == STATE_FINISHED); |
| } |
| |
| } // namespace tcu |