You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
509 lines
12 KiB
509 lines
12 KiB
/*-------------------------------------------------------------------------
|
|
* 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
|