/*------------------------------------------------------------------------- * drawElements Quality Program Test Executor * ------------------------------------------ * * 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. *//*--------------------------------------------------------------------*/ #include "xeTestCase.hpp" using std::vector; namespace xe { const char* getTestCaseTypeName (TestCaseType caseType) { switch (caseType) { case TESTCASETYPE_SELF_VALIDATE: return "SelfValidate"; case TESTCASETYPE_CAPABILITY: return "Capability"; case TESTCASETYPE_ACCURACY: return "Accuracy"; case TESTCASETYPE_PERFORMANCE: return "Performance"; default: DE_ASSERT(false); return DE_NULL; } } static inline int getFirstComponentLength (const char* path) { int compLen = 0; while (path[compLen] != 0 && path[compLen] != '.') compLen++; return compLen; } static bool compareNameToPathComponent (const char* name, const char* path, int compLen) { for (int pos = 0; pos < compLen; pos++) { if (name[pos] != path[pos]) return false; } if (name[compLen] != 0) return false; return true; } static void splitPath (const char* path, std::vector<std::string>& components) { std::string pathStr (path); int compStart = 0; for (int pos = 0; pos < (int)pathStr.length(); pos++) { if (pathStr[pos] == '.') { components.push_back(pathStr.substr(compStart, pos-compStart)); compStart = pos+1; } } DE_ASSERT(compStart < (int)pathStr.length()); components.push_back(pathStr.substr(compStart)); } // TestNode TestNode::TestNode (TestGroup* parent, TestNodeType nodeType, const char* name, const char* desc) : m_parent (parent) , m_nodeType (nodeType) , m_name (name) , m_description (desc) { if (m_parent) { // Verify that the name is unique. if (parent->m_childNames.find(name) != parent->m_childNames.end()) throw Error(std::string("Duplicate node '") + name + "' in '" + parent->getFullPath()); m_parent->m_children.push_back(this); m_parent->m_childNames.insert(name); } } void TestNode::getFullPath (std::string& dst) const { dst.clear(); int nameLen = 0; const TestNode* curNode = this; for (;;) { nameLen += (int)curNode->m_name.length(); DE_ASSERT(curNode->m_parent); if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT) { nameLen += 1; curNode = curNode->m_parent; } else break; } dst.resize(nameLen); curNode = this; int pos = nameLen; for (;;) { std::copy(curNode->m_name.begin(), curNode->m_name.end(), dst.begin()+(pos-curNode->m_name.length())); pos -= (int)curNode->m_name.length(); DE_ASSERT(curNode->m_parent); if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT) { dst[--pos] = '.'; curNode = curNode->m_parent; } else break; } } const TestNode* TestNode::find (const char* path) const { if (m_nodeType == TESTNODETYPE_ROOT) { // Don't need to consider current node. return static_cast<const TestGroup*>(this)->findChildNode(path); } else { // Check if first component matches this node. int compLen = getFirstComponentLength(path); XE_CHECK(compLen > 0); if (compareNameToPathComponent(getName(), path, compLen)) { if (path[compLen] == 0) return this; else if (getNodeType() == TESTNODETYPE_GROUP) return static_cast<const TestGroup*>(this)->findChildNode(path + compLen + 1); else return DE_NULL; } else return DE_NULL; } } TestNode* TestNode::find (const char* path) { return const_cast<TestNode*>(const_cast<const TestNode*>(this)->find(path)); } // TestGroup TestGroup::TestGroup (TestGroup* parent, TestNodeType nodeType, const char* name, const char* description) : TestNode(parent, nodeType, name, description) { DE_ASSERT(nodeType == TESTNODETYPE_GROUP || nodeType == TESTNODETYPE_ROOT); DE_ASSERT(!parent == (nodeType == TESTNODETYPE_ROOT)); } TestGroup::~TestGroup (void) { for (std::vector<TestNode*>::iterator i = m_children.begin(); i != m_children.end(); i++) delete *i; } TestGroup* TestGroup::createGroup (const char* name, const char* description) { return new TestGroup(this, TESTNODETYPE_GROUP, name, description); } TestCase* TestGroup::createCase (TestCaseType caseType, const char* name, const char* description) { return TestCase::createAsChild(this, caseType, name, description); } const TestNode* TestGroup::findChildNode (const char* path) const { int compLen = getFirstComponentLength(path); XE_CHECK(compLen > 0); // Try to find matching children. const TestNode* matchingNode = DE_NULL; for (vector<TestNode*>::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++) { if (compareNameToPathComponent((*iter)->getName(), path, compLen)) { matchingNode = *iter; break; } } if (matchingNode) { if (path[compLen] == 0) return matchingNode; // Last element in path, return matching node. else if (matchingNode->getNodeType() == TESTNODETYPE_GROUP) return static_cast<const TestGroup*>(matchingNode)->findChildNode(path + compLen + 1); else return DE_NULL; } else return DE_NULL; } // TestRoot TestRoot::TestRoot (void) : TestGroup(DE_NULL, TESTNODETYPE_ROOT, "", "") { } // TestCase TestCase* TestCase::createAsChild(TestGroup* parent, TestCaseType caseType, const char *name, const char *description) { return new TestCase(parent, caseType, name, description); } TestCase::TestCase (TestGroup* parent, TestCaseType caseType, const char* name, const char* description) : TestNode (parent, TESTNODETYPE_TEST_CASE, name, description) , m_caseType (caseType) { } TestCase::~TestCase (void) { } // TestHierarchyBuilder helpers void addChildGroupsToMap (std::map<std::string, TestGroup*>& groupMap, TestGroup* group) { for (int ndx = 0; ndx < group->getNumChildren(); ndx++) { TestNode* node = group->getChild(ndx); if (node->getNodeType() == TESTNODETYPE_GROUP) { TestGroup* childGroup = static_cast<TestGroup*>(node); std::string fullPath; childGroup->getFullPath(fullPath); groupMap.insert(std::make_pair(fullPath, childGroup)); addChildGroupsToMap(groupMap, childGroup); } } } // TestHierarchyBuilder TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root) : m_root(root) { addChildGroupsToMap(m_groupMap, root); } TestHierarchyBuilder::~TestHierarchyBuilder (void) { } TestCase* TestHierarchyBuilder::createCase (const char* path, TestCaseType caseType) { // \todo [2012-09-05 pyry] This can be done with less string manipulations. std::vector<std::string> components; splitPath(path, components); DE_ASSERT(!components.empty()); // Create all parents if necessary. TestGroup* curGroup = m_root; std::string curGroupPath; for (int ndx = 0; ndx < (int)components.size()-1; ndx++) { if (!curGroupPath.empty()) curGroupPath += "."; curGroupPath += components[ndx]; std::map<std::string, TestGroup*>::const_iterator groupPos = m_groupMap.find(curGroupPath); if (groupPos == m_groupMap.end()) { TestGroup* newGroup = curGroup->createGroup(components[ndx].c_str(), "" /* description */); m_groupMap.insert(std::make_pair(curGroupPath, newGroup)); curGroup = newGroup; } else curGroup = groupPos->second; } return curGroup->createCase(caseType, components.back().c_str(), "" /* description */); } // TestSet helpers static void addNodeAndParents (std::set<const TestNode*>& nodeSet, const TestNode* node) { while (node != DE_NULL) { nodeSet.insert(node); node = node->getParent(); } } static void addChildren (std::set<const TestNode*>& nodeSet, const TestGroup* group) { for (int ndx = 0; ndx < group->getNumChildren(); ndx++) { const TestNode* child = group->getChild(ndx); nodeSet.insert(child); if (child->getNodeType() == TESTNODETYPE_GROUP) addChildren(nodeSet, static_cast<const TestGroup*>(child)); } } static void removeChildren (std::set<const TestNode*>& nodeSet, const TestGroup* group) { for (int ndx = 0; ndx < group->getNumChildren(); ndx++) { const TestNode* child = group->getChild(ndx); nodeSet.erase(child); if (child->getNodeType() == TESTNODETYPE_GROUP) removeChildren(nodeSet, static_cast<const TestGroup*>(child)); } } static bool hasChildrenInSet (const std::set<const TestNode*>& nodeSet, const TestGroup* group) { for (int ndx = 0; ndx < group->getNumChildren(); ndx++) { if (nodeSet.find(group->getChild(ndx)) != nodeSet.end()) return true; } return false; } static void removeEmptyGroups (std::set<const TestNode*>& nodeSet, const TestGroup* group) { if (!hasChildrenInSet(nodeSet, group)) { nodeSet.erase(group); if (group->getParent() != DE_NULL) removeEmptyGroups(nodeSet, group->getParent()); } } // TestSet void TestSet::add (const TestNode* node) { if (node->getNodeType() == TESTNODETYPE_TEST_CASE) addCase(static_cast<const TestCase*>(node)); else { XE_CHECK(node->getNodeType() == TESTNODETYPE_GROUP || node->getNodeType() == TESTNODETYPE_ROOT); addGroup(static_cast<const TestGroup*>(node)); } } void TestSet::addCase (const TestCase* testCase) { addNodeAndParents(m_set, testCase); } void TestSet::addGroup (const TestGroup* testGroup) { addNodeAndParents(m_set, testGroup); addChildren(m_set, testGroup); } void TestSet::remove (const TestNode* node) { if (node->getNodeType() == TESTNODETYPE_TEST_CASE) removeCase(static_cast<const TestCase*>(node)); else { XE_CHECK(node->getNodeType() == TESTNODETYPE_GROUP || node->getNodeType() == TESTNODETYPE_ROOT); removeGroup(static_cast<const TestGroup*>(node)); } } void TestSet::removeCase (const TestCase* testCase) { if (m_set.find(testCase) != m_set.end()) { m_set.erase(testCase); removeEmptyGroups(m_set, testCase->getParent()); } } void TestSet::removeGroup (const TestGroup* testGroup) { if (m_set.find(testGroup) != m_set.end()) { m_set.erase(testGroup); removeChildren(m_set, testGroup); if (testGroup->getParent() != DE_NULL) removeEmptyGroups(m_set, testGroup->getParent()); } } // ConstTestNodeIterator ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root) : m_root(root) { } ConstTestNodeIterator ConstTestNodeIterator::begin (const TestNode* root) { ConstTestNodeIterator iter(root); iter.m_iterStack.push_back(GroupState(DE_NULL)); return iter; } ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root) { DE_UNREF(root); return ConstTestNodeIterator(root); } ConstTestNodeIterator& ConstTestNodeIterator::operator++ (void) { DE_ASSERT(!m_iterStack.empty()); const TestNode* curNode = **this; TestNodeType curNodeType = curNode->getNodeType(); if ((curNodeType == TESTNODETYPE_GROUP || curNodeType == TESTNODETYPE_ROOT) && static_cast<const TestGroup*>(curNode)->getNumChildren() > 0) { m_iterStack.push_back(GroupState(static_cast<const TestGroup*>(curNode))); } else { for (;;) { const TestGroup* group = m_iterStack.back().group; int& childNdx = m_iterStack.back().childNdx; int numChildren = group ? group->getNumChildren() : 1; childNdx += 1; if (childNdx == numChildren) { m_iterStack.pop_back(); if (m_iterStack.empty()) break; } else break; } } return *this; } ConstTestNodeIterator ConstTestNodeIterator::operator++ (int) { ConstTestNodeIterator copy(*this); ++(*this); return copy; } const TestNode* ConstTestNodeIterator::operator* (void) const { DE_ASSERT(!m_iterStack.empty()); if (m_iterStack.size() == 1) { DE_ASSERT(m_iterStack[0].group == DE_NULL && m_iterStack[0].childNdx == 0); return m_root; } else return m_iterStack.back().group->getChild(m_iterStack.back().childNdx); } bool ConstTestNodeIterator::operator!= (const ConstTestNodeIterator& other) const { return m_root != other.m_root || m_iterStack != other.m_iterStack; } } // xe