| /*------------------------------------------------------------------------- | |
| * 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 |