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 
87 TestNode::TestNode (TestGroup* parent, TestNodeType nodeType, const char* name)
88 	: m_parent		(parent)
89 	, m_nodeType	(nodeType)
90 	, m_name		(name)
91 {
92 	if (m_parent)
93 	{
94 		// Verify that the name is unique.
95 		if (parent->m_childNames.find(name) != parent->m_childNames.end())
96 			throw Error(std::string("Duplicate node '") + name + "' in '" + parent->getFullPath());
97 
98 		m_parent->m_children.push_back(this);
99 		m_parent->m_childNames.insert(name);
100 	}
101 }
102 
getFullPath(std::string& dst) const103 void TestNode::getFullPath (std::string& dst) const
104 {
105 	dst.clear();
106 
107 	int				nameLen	= 0;
108 	const TestNode*	curNode	= this;
109 
110 	for (;;)
111 	{
112 		nameLen += (int)curNode->m_name.length();
113 
114 		DE_ASSERT(curNode->m_parent);
115 		if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT)
116 		{
117 			nameLen += 1;
118 			curNode  = curNode->m_parent;
119 		}
120 		else
121 			break;
122 	}
123 
124 	dst.resize(nameLen);
125 
126 	curNode = this;
127 	int pos = nameLen;
128 
129 	for (;;)
130 	{
131 		std::copy(curNode->m_name.begin(), curNode->m_name.end(), dst.begin()+(pos-curNode->m_name.length()));
132 		pos -= (int)curNode->m_name.length();
133 
134 		DE_ASSERT(curNode->m_parent);
135 		if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT)
136 		{
137 			dst[--pos] = '.';
138 			curNode = curNode->m_parent;
139 		}
140 		else
141 			break;
142 	}
143 }
144 
find(const char* path) const145 const TestNode* TestNode::find (const char* path) const
146 {
147 	if (m_nodeType == TESTNODETYPE_ROOT)
148 	{
149 		// Don't need to consider current node.
150 		return static_cast<const TestGroup*>(this)->findChildNode(path);
151 	}
152 	else
153 	{
154 		// Check if first component matches this node.
155 		int compLen = getFirstComponentLength(path);
156 		XE_CHECK(compLen > 0);
157 
158 		if (compareNameToPathComponent(getName(), path, compLen))
159 		{
160 			if (path[compLen] == 0)
161 				return this;
162 			else if (getNodeType() == TESTNODETYPE_GROUP)
163 				return static_cast<const TestGroup*>(this)->findChildNode(path + compLen + 1);
164 			else
165 				return DE_NULL;
166 		}
167 		else
168 			return DE_NULL;
169 	}
170 }
171 
find(const char* path)172 TestNode* TestNode::find (const char* path)
173 {
174 	return const_cast<TestNode*>(const_cast<const TestNode*>(this)->find(path));
175 }
176 
177 // TestGroup
178 
TestGroup(TestGroup* parent, TestNodeType nodeType, const char* name)179 TestGroup::TestGroup (TestGroup* parent, TestNodeType nodeType, const char* name)
180 	: TestNode(parent, nodeType, name)
181 {
182 	DE_ASSERT(nodeType == TESTNODETYPE_GROUP || nodeType == TESTNODETYPE_ROOT);
183 	DE_ASSERT(!parent == (nodeType == TESTNODETYPE_ROOT));
184 }
185 
~TestGroup(void)186 TestGroup::~TestGroup (void)
187 {
188 	for (std::vector<TestNode*>::iterator i = m_children.begin(); i != m_children.end(); i++)
189 		delete *i;
190 }
191 
createGroup(const char* name)192 TestGroup* TestGroup::createGroup (const char* name)
193 {
194 	return new TestGroup(this, TESTNODETYPE_GROUP, name);
195 }
196 
createCase(TestCaseType caseType, const char* name)197 TestCase* TestGroup::createCase (TestCaseType caseType, const char* name)
198 {
199 	return TestCase::createAsChild(this, caseType, name);
200 }
201 
findChildNode(const char* path) const202 const TestNode* TestGroup::findChildNode (const char* path) const
203 {
204 	int compLen = getFirstComponentLength(path);
205 	XE_CHECK(compLen > 0);
206 
207 	// Try to find matching children.
208 	const TestNode* matchingNode = DE_NULL;
209 	for (vector<TestNode*>::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++)
210 	{
211 		if (compareNameToPathComponent((*iter)->getName(), path, compLen))
212 		{
213 			matchingNode = *iter;
214 			break;
215 		}
216 	}
217 
218 	if (matchingNode)
219 	{
220 		if (path[compLen] == 0)
221 			return matchingNode; // Last element in path, return matching node.
222 		else if (matchingNode->getNodeType() == TESTNODETYPE_GROUP)
223 			return static_cast<const TestGroup*>(matchingNode)->findChildNode(path + compLen + 1);
224 		else
225 			return DE_NULL;
226 	}
227 	else
228 		return DE_NULL;
229 }
230 
231 // TestRoot
232 
TestRoot(void)233 TestRoot::TestRoot (void)
234 	: TestGroup(DE_NULL, TESTNODETYPE_ROOT, "")
235 {
236 }
237 
238 // TestCase
239 
createAsChild(TestGroup* parent, TestCaseType caseType, const char *name)240 TestCase* TestCase::createAsChild(TestGroup* parent, TestCaseType caseType, const char *name)
241 {
242 	return new TestCase(parent, caseType, name);
243 }
244 
TestCase(TestGroup* parent, TestCaseType caseType, const char* name)245 TestCase::TestCase (TestGroup* parent, TestCaseType caseType, const char* name)
246 	: TestNode		(parent, TESTNODETYPE_TEST_CASE, name)
247 	, m_caseType	(caseType)
248 {
249 }
250 
~TestCase(void)251 TestCase::~TestCase (void)
252 {
253 }
254 
255 // TestHierarchyBuilder helpers
256 
addChildGroupsToMap(std::map<std::string, TestGroup*>& groupMap, TestGroup* group)257 void addChildGroupsToMap (std::map<std::string, TestGroup*>& groupMap, TestGroup* group)
258 {
259 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
260 	{
261 		TestNode* node = group->getChild(ndx);
262 		if (node->getNodeType() == TESTNODETYPE_GROUP)
263 		{
264 			TestGroup*	childGroup	= static_cast<TestGroup*>(node);
265 			std::string	fullPath;
266 			childGroup->getFullPath(fullPath);
267 
268 			groupMap.insert(std::make_pair(fullPath, childGroup));
269 			addChildGroupsToMap(groupMap, childGroup);
270 		}
271 	}
272 }
273 
274 // TestHierarchyBuilder
275 
TestHierarchyBuilder(TestRoot* root)276 TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root)
277 	: m_root(root)
278 {
279 	addChildGroupsToMap(m_groupMap, root);
280 }
281 
~TestHierarchyBuilder(void)282 TestHierarchyBuilder::~TestHierarchyBuilder (void)
283 {
284 }
285 
createCase(const char* path, TestCaseType caseType)286 TestCase* TestHierarchyBuilder::createCase (const char* path, TestCaseType caseType)
287 {
288 	// \todo [2012-09-05 pyry] This can be done with less string manipulations.
289 	std::vector<std::string> components;
290 	splitPath(path, components);
291 	DE_ASSERT(!components.empty());
292 
293 	// Create all parents if necessary.
294 	TestGroup*	curGroup		= m_root;
295 	std::string	curGroupPath;
296 	for (int ndx = 0; ndx < (int)components.size()-1; ndx++)
297 	{
298 		if (!curGroupPath.empty())
299 			curGroupPath += ".";
300 		curGroupPath += components[ndx];
301 
302 		std::map<std::string, TestGroup*>::const_iterator groupPos = m_groupMap.find(curGroupPath);
303 		if (groupPos == m_groupMap.end())
304 		{
305 			TestGroup* newGroup = curGroup->createGroup(components[ndx].c_str());
306 			m_groupMap.insert(std::make_pair(curGroupPath, newGroup));
307 			curGroup = newGroup;
308 		}
309 		else
310 			curGroup = groupPos->second;
311 	}
312 
313 	return curGroup->createCase(caseType, components.back().c_str());
314 }
315 
316 // TestSet helpers
317 
addNodeAndParents(std::set<const TestNode*>& nodeSet, const TestNode* node)318 static void addNodeAndParents (std::set<const TestNode*>& nodeSet, const TestNode* node)
319 {
320 	while (node != DE_NULL)
321 	{
322 		nodeSet.insert(node);
323 		node = node->getParent();
324 	}
325 }
326 
addChildren(std::set<const TestNode*>& nodeSet, const TestGroup* group)327 static void addChildren (std::set<const TestNode*>& nodeSet, const TestGroup* group)
328 {
329 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
330 	{
331 		const TestNode* child = group->getChild(ndx);
332 		nodeSet.insert(child);
333 
334 		if (child->getNodeType() == TESTNODETYPE_GROUP)
335 			addChildren(nodeSet, static_cast<const TestGroup*>(child));
336 	}
337 }
338 
removeChildren(std::set<const TestNode*>& nodeSet, const TestGroup* group)339 static void removeChildren (std::set<const TestNode*>& nodeSet, const TestGroup* group)
340 {
341 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
342 	{
343 		const TestNode* child = group->getChild(ndx);
344 		nodeSet.erase(child);
345 
346 		if (child->getNodeType() == TESTNODETYPE_GROUP)
347 			removeChildren(nodeSet, static_cast<const TestGroup*>(child));
348 	}
349 }
350 
hasChildrenInSet(const std::set<const TestNode*>& nodeSet, const TestGroup* group)351 static bool hasChildrenInSet (const std::set<const TestNode*>& nodeSet, const TestGroup* group)
352 {
353 	for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
354 	{
355 		if (nodeSet.find(group->getChild(ndx)) != nodeSet.end())
356 			return true;
357 	}
358 	return false;
359 }
360 
removeEmptyGroups(std::set<const TestNode*>& nodeSet, const TestGroup* group)361 static void removeEmptyGroups (std::set<const TestNode*>& nodeSet, const TestGroup* group)
362 {
363 	if (!hasChildrenInSet(nodeSet, group))
364 	{
365 		nodeSet.erase(group);
366 		if (group->getParent() != DE_NULL)
367 			removeEmptyGroups(nodeSet, group->getParent());
368 	}
369 }
370 
371 // TestSet
372 
add(const TestNode* node)373 void TestSet::add (const TestNode* node)
374 {
375 	if (node->getNodeType() == TESTNODETYPE_TEST_CASE)
376 		addCase(static_cast<const TestCase*>(node));
377 	else
378 	{
379 		XE_CHECK(node->getNodeType() == TESTNODETYPE_GROUP ||
380 				  node->getNodeType() == TESTNODETYPE_ROOT);
381 		addGroup(static_cast<const TestGroup*>(node));
382 	}
383 }
384 
addCase(const TestCase* testCase)385 void TestSet::addCase (const TestCase* testCase)
386 {
387 	addNodeAndParents(m_set, testCase);
388 }
389 
addGroup(const TestGroup* testGroup)390 void TestSet::addGroup (const TestGroup* testGroup)
391 {
392 	addNodeAndParents(m_set, testGroup);
393 	addChildren(m_set, testGroup);
394 }
395 
remove(const TestNode* node)396 void TestSet::remove (const TestNode* node)
397 {
398 	if (node->getNodeType() == TESTNODETYPE_TEST_CASE)
399 		removeCase(static_cast<const TestCase*>(node));
400 	else
401 	{
402 		XE_CHECK(node->getNodeType() == TESTNODETYPE_GROUP ||
403 				  node->getNodeType() == TESTNODETYPE_ROOT);
404 		removeGroup(static_cast<const TestGroup*>(node));
405 	}
406 }
407 
removeCase(const TestCase* testCase)408 void TestSet::removeCase (const TestCase* testCase)
409 {
410 	if (m_set.find(testCase) != m_set.end())
411 	{
412 		m_set.erase(testCase);
413 		removeEmptyGroups(m_set, testCase->getParent());
414 	}
415 }
416 
removeGroup(const TestGroup* testGroup)417 void TestSet::removeGroup (const TestGroup* testGroup)
418 {
419 	if (m_set.find(testGroup) != m_set.end())
420 	{
421 		m_set.erase(testGroup);
422 		removeChildren(m_set, testGroup);
423 		if (testGroup->getParent() != DE_NULL)
424 			removeEmptyGroups(m_set, testGroup->getParent());
425 	}
426 }
427 
428 // ConstTestNodeIterator
429 
ConstTestNodeIterator(const TestNode* root)430 ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root)
431 	: m_root(root)
432 {
433 }
434 
begin(const TestNode* root)435 ConstTestNodeIterator ConstTestNodeIterator::begin (const TestNode* root)
436 {
437 	ConstTestNodeIterator iter(root);
438 	iter.m_iterStack.push_back(GroupState(DE_NULL));
439 	return iter;
440 }
441 
end(const TestNode* root)442 ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root)
443 {
444 	DE_UNREF(root);
445 	return ConstTestNodeIterator(root);
446 }
447 
operator ++(void)448 ConstTestNodeIterator& ConstTestNodeIterator::operator++ (void)
449 {
450 	DE_ASSERT(!m_iterStack.empty());
451 
452 	const TestNode*	curNode			= **this;
453 	TestNodeType	curNodeType		= curNode->getNodeType();
454 
455 	if ((curNodeType == TESTNODETYPE_GROUP || curNodeType == TESTNODETYPE_ROOT) &&
456 		static_cast<const TestGroup*>(curNode)->getNumChildren() > 0)
457 	{
458 		m_iterStack.push_back(GroupState(static_cast<const TestGroup*>(curNode)));
459 	}
460 	else
461 	{
462 		for (;;)
463 		{
464 			const TestGroup*	group		= m_iterStack.back().group;
465 			int&				childNdx	= m_iterStack.back().childNdx;
466 			int					numChildren	= group ? group->getNumChildren() : 1;
467 
468 			childNdx += 1;
469 			if (childNdx == numChildren)
470 			{
471 				m_iterStack.pop_back();
472 				if (m_iterStack.empty())
473 					break;
474 			}
475 			else
476 				break;
477 		}
478 	}
479 
480 	return *this;
481 }
482 
operator ++(int)483 ConstTestNodeIterator ConstTestNodeIterator::operator++ (int)
484 {
485 	ConstTestNodeIterator copy(*this);
486 	++(*this);
487 	return copy;
488 }
489 
operator *(void) const490 const TestNode* ConstTestNodeIterator::operator* (void) const
491 {
492 	DE_ASSERT(!m_iterStack.empty());
493 	if (m_iterStack.size() == 1)
494 	{
495 		DE_ASSERT(m_iterStack[0].group == DE_NULL && m_iterStack[0].childNdx == 0);
496 		return m_root;
497 	}
498 	else
499 		return m_iterStack.back().group->getChild(m_iterStack.back().childNdx);
500 }
501 
operator !=(const ConstTestNodeIterator& other) const502 bool ConstTestNodeIterator::operator!= (const ConstTestNodeIterator& other) const
503 {
504 	return m_root != other.m_root || m_iterStack != other.m_iterStack;
505 }
506 
507 } // xe
508