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 
getTestCaseTypeName(TestCaseType caseType)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 
getFirstComponentLength(const char * path)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 
compareNameToPathComponent(const char * name,const char * path,int compLen)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 
splitPath(const char * path,std::vector<std::string> & components)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 
TestNode(TestGroup * parent,TestNodeType nodeType,const char * name,const char * desc)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 
getFullPath(std::string & dst) const104 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 
find(const char * path) const146 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 
find(const char * path)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 
TestGroup(TestGroup * parent,TestNodeType nodeType,const char * name,const char * description)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 
~TestGroup(void)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 
createGroup(const char * name,const char * description)193 TestGroup* TestGroup::createGroup (const char* name, const char* description)
194 {
195 	return new TestGroup(this, TESTNODETYPE_GROUP, name, description);
196 }
197 
createCase(TestCaseType caseType,const char * name,const char * description)198 TestCase* TestGroup::createCase (TestCaseType caseType, const char* name, const char* description)
199 {
200 	return TestCase::createAsChild(this, caseType, name, description);
201 }
202 
findChildNode(const char * path) const203 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 
TestRoot(void)234 TestRoot::TestRoot (void)
235 	: TestGroup(DE_NULL, TESTNODETYPE_ROOT, "", "")
236 {
237 }
238 
239 // TestCase
240 
createAsChild(TestGroup * parent,TestCaseType caseType,const char * name,const char * description)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 
TestCase(TestGroup * parent,TestCaseType caseType,const char * name,const char * description)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 
~TestCase(void)252 TestCase::~TestCase (void)
253 {
254 }
255 
256 // TestHierarchyBuilder helpers
257 
addChildGroupsToMap(std::map<std::string,TestGroup * > & groupMap,TestGroup * group)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 
TestHierarchyBuilder(TestRoot * root)277 TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root)
278 	: m_root(root)
279 {
280 	addChildGroupsToMap(m_groupMap, root);
281 }
282 
~TestHierarchyBuilder(void)283 TestHierarchyBuilder::~TestHierarchyBuilder (void)
284 {
285 }
286 
createCase(const char * path,TestCaseType caseType)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 
addNodeAndParents(std::set<const TestNode * > & nodeSet,const TestNode * node)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 
addChildren(std::set<const TestNode * > & nodeSet,const TestGroup * group)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 
removeChildren(std::set<const TestNode * > & nodeSet,const TestGroup * group)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 
hasChildrenInSet(const std::set<const TestNode * > & nodeSet,const TestGroup * group)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 
removeEmptyGroups(std::set<const TestNode * > & nodeSet,const TestGroup * group)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 
add(const TestNode * node)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 
addCase(const TestCase * testCase)386 void TestSet::addCase (const TestCase* testCase)
387 {
388 	addNodeAndParents(m_set, testCase);
389 }
390 
addGroup(const TestGroup * testGroup)391 void TestSet::addGroup (const TestGroup* testGroup)
392 {
393 	addNodeAndParents(m_set, testGroup);
394 	addChildren(m_set, testGroup);
395 }
396 
remove(const TestNode * node)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 
removeCase(const TestCase * testCase)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 
removeGroup(const TestGroup * testGroup)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 
ConstTestNodeIterator(const TestNode * root)431 ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root)
432 	: m_root(root)
433 {
434 }
435 
begin(const TestNode * root)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 
end(const TestNode * root)443 ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root)
444 {
445 	DE_UNREF(root);
446 	return ConstTestNodeIterator(root);
447 }
448 
operator ++(void)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 
operator ++(int)484 ConstTestNodeIterator ConstTestNodeIterator::operator++ (int)
485 {
486 	ConstTestNodeIterator copy(*this);
487 	++(*this);
488 	return copy;
489 }
490 
operator *(void) const491 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 
operator !=(const ConstTestNodeIterator & other) const503 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