Home | History | Annotate | Download | only in executor
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program Test Executor
      3  * ------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Test case.
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "xeTestCase.hpp"
     25 
     26 using std::vector;
     27 
     28 namespace xe
     29 {
     30 
     31 const char* getTestCaseTypeName (TestCaseType caseType)
     32 {
     33 	switch (caseType)
     34 	{
     35 		case TESTCASETYPE_SELF_VALIDATE:	return "SelfValidate";
     36 		case TESTCASETYPE_CAPABILITY:		return "Capability";
     37 		case TESTCASETYPE_ACCURACY:			return "Accuracy";
     38 		case TESTCASETYPE_PERFORMANCE:		return "Performance";
     39 		default:
     40 			DE_ASSERT(false);
     41 			return DE_NULL;
     42 	}
     43 }
     44 
     45 static inline int getFirstComponentLength (const char* path)
     46 {
     47 	int compLen = 0;
     48 	while (path[compLen] != 0 && path[compLen] != '.')
     49 		compLen++;
     50 	return compLen;
     51 }
     52 
     53 static bool compareNameToPathComponent (const char* name, const char* path, int compLen)
     54 {
     55 	for (int pos = 0; pos < compLen; pos++)
     56 	{
     57 		if (name[pos] != path[pos])
     58 			return false;
     59 	}
     60 
     61 	if (name[compLen] != 0)
     62 		return false;
     63 
     64 	return true;
     65 }
     66 
     67 static void splitPath (const char* path, std::vector<std::string>& components)
     68 {
     69 	std::string	pathStr		(path);
     70 	int			compStart	= 0;
     71 
     72 	for (int pos = 0; pos < (int)pathStr.length(); pos++)
     73 	{
     74 		if (pathStr[pos] == '.')
     75 		{
     76 			components.push_back(pathStr.substr(compStart, pos-compStart));
     77 			compStart = pos+1;
     78 		}
     79 	}
     80 
     81 	DE_ASSERT(compStart < (int)pathStr.length());
     82 	components.push_back(pathStr.substr(compStart));
     83 }
     84 
     85 // TestNode
     86 
     87 TestNode::TestNode (TestGroup* parent, TestNodeType nodeType, const char* name, const char* desc)
     88 	: m_parent		(parent)
     89 	, m_nodeType	(nodeType)
     90 	, m_name		(name)
     91 	, m_description	(desc)
     92 {
     93 	if (m_parent)
     94 	{
     95 		// Verify that the name is unique.
     96 		if (parent->m_childNames.find(name) != parent->m_childNames.end())
     97 			throw Error(std::string("Duplicate node '") + name + "' in '" + parent->getFullPath());
     98 
     99 		m_parent->m_children.push_back(this);
    100 		m_parent->m_childNames.insert(name);
    101 	}
    102 }
    103 
    104 void TestNode::getFullPath (std::string& dst) const
    105 {
    106 	dst.clear();
    107 
    108 	int				nameLen	= 0;
    109 	const TestNode*	curNode	= this;
    110 
    111 	for (;;)
    112 	{
    113 		nameLen += (int)curNode->m_name.length();
    114 
    115 		DE_ASSERT(curNode->m_parent);
    116 		if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT)
    117 		{
    118 			nameLen += 1;
    119 			curNode  = curNode->m_parent;
    120 		}
    121 		else
    122 			break;
    123 	}
    124 
    125 	dst.resize(nameLen);
    126 
    127 	curNode = this;
    128 	int pos = nameLen;
    129 
    130 	for (;;)
    131 	{
    132 		std::copy(curNode->m_name.begin(), curNode->m_name.end(), dst.begin()+(pos-curNode->m_name.length()));
    133 		pos -= (int)curNode->m_name.length();
    134 
    135 		DE_ASSERT(curNode->m_parent);
    136 		if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT)
    137 		{
    138 			dst[--pos] = '.';
    139 			curNode = curNode->m_parent;
    140 		}
    141 		else
    142 			break;
    143 	}
    144 }
    145 
    146 const TestNode* TestNode::find (const char* path) const
    147 {
    148 	if (m_nodeType == TESTNODETYPE_ROOT)
    149 	{
    150 		// Don't need to consider current node.
    151 		return static_cast<const TestGroup*>(this)->findChildNode(path);
    152 	}
    153 	else
    154 	{
    155 		// Check if first component matches this node.
    156 		int compLen = getFirstComponentLength(path);
    157 		XE_CHECK(compLen > 0);
    158 
    159 		if (compareNameToPathComponent(getName(), path, compLen))
    160 		{
    161 			if (path[compLen] == 0)
    162 				return this;
    163 			else if (getNodeType() == TESTNODETYPE_GROUP)
    164 				return static_cast<const TestGroup*>(this)->findChildNode(path + compLen + 1);
    165 			else
    166 				return DE_NULL;
    167 		}
    168 		else
    169 			return DE_NULL;
    170 	}
    171 }
    172 
    173 TestNode* TestNode::find (const char* path)
    174 {
    175 	return const_cast<TestNode*>(const_cast<const TestNode*>(this)->find(path));
    176 }
    177 
    178 // TestGroup
    179 
    180 TestGroup::TestGroup (TestGroup* parent, TestNodeType nodeType, const char* name, const char* description)
    181 	: TestNode(parent, nodeType, name, description)
    182 {
    183 	DE_ASSERT(nodeType == TESTNODETYPE_GROUP || nodeType == TESTNODETYPE_ROOT);
    184 	DE_ASSERT(!parent == (nodeType == TESTNODETYPE_ROOT));
    185 }
    186 
    187 TestGroup::~TestGroup (void)
    188 {
    189 	for (std::vector<TestNode*>::iterator i = m_children.begin(); i != m_children.end(); i++)
    190 		delete *i;
    191 }
    192 
    193 TestGroup* TestGroup::createGroup (const char* name, const char* description)
    194 {
    195 	return new TestGroup(this, TESTNODETYPE_GROUP, name, description);
    196 }
    197 
    198 TestCase* TestGroup::createCase (TestCaseType caseType, const char* name, const char* description)
    199 {
    200 	return TestCase::createAsChild(this, caseType, name, description);
    201 }
    202 
    203 const TestNode* TestGroup::findChildNode (const char* path) const
    204 {
    205 	int compLen = getFirstComponentLength(path);
    206 	XE_CHECK(compLen > 0);
    207 
    208 	// Try to find matching children.
    209 	const TestNode* matchingNode = DE_NULL;
    210 	for (vector<TestNode*>::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++)
    211 	{
    212 		if (compareNameToPathComponent((*iter)->getName(), path, compLen))
    213 		{
    214 			matchingNode = *iter;
    215 			break;
    216 		}
    217 	}
    218 
    219 	if (matchingNode)
    220 	{
    221 		if (path[compLen] == 0)
    222 			return matchingNode; // Last element in path, return matching node.
    223 		else if (matchingNode->getNodeType() == TESTNODETYPE_GROUP)
    224 			return static_cast<const TestGroup*>(matchingNode)->findChildNode(path + compLen + 1);
    225 		else
    226 			return DE_NULL;
    227 	}
    228 	else
    229 		return DE_NULL;
    230 }
    231 
    232 // TestRoot
    233 
    234 TestRoot::TestRoot (void)
    235 	: TestGroup(DE_NULL, TESTNODETYPE_ROOT, "", "")
    236 {
    237 }
    238 
    239 // TestCase
    240 
    241 TestCase* TestCase::createAsChild(TestGroup* parent, TestCaseType caseType, const char *name, const char *description)
    242 {
    243 	return new TestCase(parent, caseType, name, description);
    244 }
    245 
    246 TestCase::TestCase (TestGroup* parent, TestCaseType caseType, const char* name, const char* description)
    247 	: TestNode		(parent, TESTNODETYPE_TEST_CASE, name, description)
    248 	, m_caseType	(caseType)
    249 {
    250 }
    251 
    252 TestCase::~TestCase (void)
    253 {
    254 }
    255 
    256 // TestHierarchyBuilder helpers
    257 
    258 void addChildGroupsToMap (std::map<std::string, TestGroup*>& groupMap, TestGroup* group)
    259 {
    260 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
    261 	{
    262 		TestNode* node = group->getChild(ndx);
    263 		if (node->getNodeType() == TESTNODETYPE_GROUP)
    264 		{
    265 			TestGroup*	childGroup	= static_cast<TestGroup*>(node);
    266 			std::string	fullPath;
    267 			childGroup->getFullPath(fullPath);
    268 
    269 			groupMap.insert(std::make_pair(fullPath, childGroup));
    270 			addChildGroupsToMap(groupMap, childGroup);
    271 		}
    272 	}
    273 }
    274 
    275 // TestHierarchyBuilder
    276 
    277 TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root)
    278 	: m_root(root)
    279 {
    280 	addChildGroupsToMap(m_groupMap, root);
    281 }
    282 
    283 TestHierarchyBuilder::~TestHierarchyBuilder (void)
    284 {
    285 }
    286 
    287 TestCase* TestHierarchyBuilder::createCase (const char* path, TestCaseType caseType)
    288 {
    289 	// \todo [2012-09-05 pyry] This can be done with less string manipulations.
    290 	std::vector<std::string> components;
    291 	splitPath(path, components);
    292 	DE_ASSERT(!components.empty());
    293 
    294 	// Create all parents if necessary.
    295 	TestGroup*	curGroup		= m_root;
    296 	std::string	curGroupPath;
    297 	for (int ndx = 0; ndx < (int)components.size()-1; ndx++)
    298 	{
    299 		if (!curGroupPath.empty())
    300 			curGroupPath += ".";
    301 		curGroupPath += components[ndx];
    302 
    303 		std::map<std::string, TestGroup*>::const_iterator groupPos = m_groupMap.find(curGroupPath);
    304 		if (groupPos == m_groupMap.end())
    305 		{
    306 			TestGroup* newGroup = curGroup->createGroup(components[ndx].c_str(), "" /* description */);
    307 			m_groupMap.insert(std::make_pair(curGroupPath, newGroup));
    308 			curGroup = newGroup;
    309 		}
    310 		else
    311 			curGroup = groupPos->second;
    312 	}
    313 
    314 	return curGroup->createCase(caseType, components.back().c_str(), "" /* description */);
    315 }
    316 
    317 // TestSet helpers
    318 
    319 static void addNodeAndParents (std::set<const TestNode*>& nodeSet, const TestNode* node)
    320 {
    321 	while (node != DE_NULL)
    322 	{
    323 		nodeSet.insert(node);
    324 		node = node->getParent();
    325 	}
    326 }
    327 
    328 static void addChildren (std::set<const TestNode*>& nodeSet, const TestGroup* group)
    329 {
    330 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
    331 	{
    332 		const TestNode* child = group->getChild(ndx);
    333 		nodeSet.insert(child);
    334 
    335 		if (child->getNodeType() == TESTNODETYPE_GROUP)
    336 			addChildren(nodeSet, static_cast<const TestGroup*>(child));
    337 	}
    338 }
    339 
    340 static void removeChildren (std::set<const TestNode*>& nodeSet, const TestGroup* group)
    341 {
    342 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
    343 	{
    344 		const TestNode* child = group->getChild(ndx);
    345 		nodeSet.erase(child);
    346 
    347 		if (child->getNodeType() == TESTNODETYPE_GROUP)
    348 			removeChildren(nodeSet, static_cast<const TestGroup*>(child));
    349 	}
    350 }
    351 
    352 static bool hasChildrenInSet (const std::set<const TestNode*>& nodeSet, const TestGroup* group)
    353 {
    354 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
    355 	{
    356 		if (nodeSet.find(group->getChild(ndx)) != nodeSet.end())
    357 			return true;
    358 	}
    359 	return false;
    360 }
    361 
    362 static void removeEmptyGroups (std::set<const TestNode*>& nodeSet, const TestGroup* group)
    363 {
    364 	if (!hasChildrenInSet(nodeSet, group))
    365 	{
    366 		nodeSet.erase(group);
    367 		if (group->getParent() != DE_NULL)
    368 			removeEmptyGroups(nodeSet, group->getParent());
    369 	}
    370 }
    371 
    372 // TestSet
    373 
    374 void TestSet::add (const TestNode* node)
    375 {
    376 	if (node->getNodeType() == TESTNODETYPE_TEST_CASE)
    377 		addCase(static_cast<const TestCase*>(node));
    378 	else
    379 	{
    380 		XE_CHECK(node->getNodeType() == TESTNODETYPE_GROUP ||
    381 				  node->getNodeType() == TESTNODETYPE_ROOT);
    382 		addGroup(static_cast<const TestGroup*>(node));
    383 	}
    384 }
    385 
    386 void TestSet::addCase (const TestCase* testCase)
    387 {
    388 	addNodeAndParents(m_set, testCase);
    389 }
    390 
    391 void TestSet::addGroup (const TestGroup* testGroup)
    392 {
    393 	addNodeAndParents(m_set, testGroup);
    394 	addChildren(m_set, testGroup);
    395 }
    396 
    397 void TestSet::remove (const TestNode* node)
    398 {
    399 	if (node->getNodeType() == TESTNODETYPE_TEST_CASE)
    400 		removeCase(static_cast<const TestCase*>(node));
    401 	else
    402 	{
    403 		XE_CHECK(node->getNodeType() == TESTNODETYPE_GROUP ||
    404 				  node->getNodeType() == TESTNODETYPE_ROOT);
    405 		removeGroup(static_cast<const TestGroup*>(node));
    406 	}
    407 }
    408 
    409 void TestSet::removeCase (const TestCase* testCase)
    410 {
    411 	if (m_set.find(testCase) != m_set.end())
    412 	{
    413 		m_set.erase(testCase);
    414 		removeEmptyGroups(m_set, testCase->getParent());
    415 	}
    416 }
    417 
    418 void TestSet::removeGroup (const TestGroup* testGroup)
    419 {
    420 	if (m_set.find(testGroup) != m_set.end())
    421 	{
    422 		m_set.erase(testGroup);
    423 		removeChildren(m_set, testGroup);
    424 		if (testGroup->getParent() != DE_NULL)
    425 			removeEmptyGroups(m_set, testGroup->getParent());
    426 	}
    427 }
    428 
    429 // ConstTestNodeIterator
    430 
    431 ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root)
    432 	: m_root(root)
    433 {
    434 }
    435 
    436 ConstTestNodeIterator ConstTestNodeIterator::begin (const TestNode* root)
    437 {
    438 	ConstTestNodeIterator iter(root);
    439 	iter.m_iterStack.push_back(GroupState(DE_NULL));
    440 	return iter;
    441 }
    442 
    443 ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root)
    444 {
    445 	DE_UNREF(root);
    446 	return ConstTestNodeIterator(root);
    447 }
    448 
    449 ConstTestNodeIterator& ConstTestNodeIterator::operator++ (void)
    450 {
    451 	DE_ASSERT(!m_iterStack.empty());
    452 
    453 	const TestNode*	curNode			= **this;
    454 	TestNodeType	curNodeType		= curNode->getNodeType();
    455 
    456 	if ((curNodeType == TESTNODETYPE_GROUP || curNodeType == TESTNODETYPE_ROOT) &&
    457 		static_cast<const TestGroup*>(curNode)->getNumChildren() > 0)
    458 	{
    459 		m_iterStack.push_back(GroupState(static_cast<const TestGroup*>(curNode)));
    460 	}
    461 	else
    462 	{
    463 		for (;;)
    464 		{
    465 			const TestGroup*	group		= m_iterStack.back().group;
    466 			int&				childNdx	= m_iterStack.back().childNdx;
    467 			int					numChildren	= group ? group->getNumChildren() : 1;
    468 
    469 			childNdx += 1;
    470 			if (childNdx == numChildren)
    471 			{
    472 				m_iterStack.pop_back();
    473 				if (m_iterStack.empty())
    474 					break;
    475 			}
    476 			else
    477 				break;
    478 		}
    479 	}
    480 
    481 	return *this;
    482 }
    483 
    484 ConstTestNodeIterator ConstTestNodeIterator::operator++ (int)
    485 {
    486 	ConstTestNodeIterator copy(*this);
    487 	++(*this);
    488 	return copy;
    489 }
    490 
    491 const TestNode* ConstTestNodeIterator::operator* (void) const
    492 {
    493 	DE_ASSERT(!m_iterStack.empty());
    494 	if (m_iterStack.size() == 1)
    495 	{
    496 		DE_ASSERT(m_iterStack[0].group == DE_NULL && m_iterStack[0].childNdx == 0);
    497 		return m_root;
    498 	}
    499 	else
    500 		return m_iterStack.back().group->getChild(m_iterStack.back().childNdx);
    501 }
    502 
    503 bool ConstTestNodeIterator::operator!= (const ConstTestNodeIterator& other) const
    504 {
    505 	return m_root != other.m_root || m_iterStack != other.m_iterStack;
    506 }
    507 
    508 } // xe
    509