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
26using std::vector;
27
28namespace xe
29{
30
31const 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
45static 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
53static 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
67static 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
87TestNode::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
103void 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
145const 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
172TestNode* TestNode::find (const char* path)
173{
174	return const_cast<TestNode*>(const_cast<const TestNode*>(this)->find(path));
175}
176
177// TestGroup
178
179TestGroup::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
186TestGroup::~TestGroup (void)
187{
188	for (std::vector<TestNode*>::iterator i = m_children.begin(); i != m_children.end(); i++)
189		delete *i;
190}
191
192TestGroup* TestGroup::createGroup (const char* name)
193{
194	return new TestGroup(this, TESTNODETYPE_GROUP, name);
195}
196
197TestCase* TestGroup::createCase (TestCaseType caseType, const char* name)
198{
199	return TestCase::createAsChild(this, caseType, name);
200}
201
202const 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
233TestRoot::TestRoot (void)
234	: TestGroup(DE_NULL, TESTNODETYPE_ROOT, "")
235{
236}
237
238// TestCase
239
240TestCase* TestCase::createAsChild(TestGroup* parent, TestCaseType caseType, const char *name)
241{
242	return new TestCase(parent, caseType, name);
243}
244
245TestCase::TestCase (TestGroup* parent, TestCaseType caseType, const char* name)
246	: TestNode		(parent, TESTNODETYPE_TEST_CASE, name)
247	, m_caseType	(caseType)
248{
249}
250
251TestCase::~TestCase (void)
252{
253}
254
255// TestHierarchyBuilder helpers
256
257void 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
276TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root)
277	: m_root(root)
278{
279	addChildGroupsToMap(m_groupMap, root);
280}
281
282TestHierarchyBuilder::~TestHierarchyBuilder (void)
283{
284}
285
286TestCase* 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
318static 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
327static 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
339static 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
351static 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
361static 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
373void 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
385void TestSet::addCase (const TestCase* testCase)
386{
387	addNodeAndParents(m_set, testCase);
388}
389
390void TestSet::addGroup (const TestGroup* testGroup)
391{
392	addNodeAndParents(m_set, testGroup);
393	addChildren(m_set, testGroup);
394}
395
396void 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
408void 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
417void 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
430ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root)
431	: m_root(root)
432{
433}
434
435ConstTestNodeIterator ConstTestNodeIterator::begin (const TestNode* root)
436{
437	ConstTestNodeIterator iter(root);
438	iter.m_iterStack.push_back(GroupState(DE_NULL));
439	return iter;
440}
441
442ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root)
443{
444	DE_UNREF(root);
445	return ConstTestNodeIterator(root);
446}
447
448ConstTestNodeIterator& 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
483ConstTestNodeIterator ConstTestNodeIterator::operator++ (int)
484{
485	ConstTestNodeIterator copy(*this);
486	++(*this);
487	return copy;
488}
489
490const 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
502bool ConstTestNodeIterator::operator!= (const ConstTestNodeIterator& other) const
503{
504	return m_root != other.m_root || m_iterStack != other.m_iterStack;
505}
506
507} // xe
508