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, 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
104void 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
146const 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
173TestNode* TestNode::find (const char* path)
174{
175	return const_cast<TestNode*>(const_cast<const TestNode*>(this)->find(path));
176}
177
178// TestGroup
179
180TestGroup::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
187TestGroup::~TestGroup (void)
188{
189	for (std::vector<TestNode*>::iterator i = m_children.begin(); i != m_children.end(); i++)
190		delete *i;
191}
192
193TestGroup* TestGroup::createGroup (const char* name, const char* description)
194{
195	return new TestGroup(this, TESTNODETYPE_GROUP, name, description);
196}
197
198TestCase* TestGroup::createCase (TestCaseType caseType, const char* name, const char* description)
199{
200	return TestCase::createAsChild(this, caseType, name, description);
201}
202
203const 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
234TestRoot::TestRoot (void)
235	: TestGroup(DE_NULL, TESTNODETYPE_ROOT, "", "")
236{
237}
238
239// TestCase
240
241TestCase* TestCase::createAsChild(TestGroup* parent, TestCaseType caseType, const char *name, const char *description)
242{
243	return new TestCase(parent, caseType, name, description);
244}
245
246TestCase::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
252TestCase::~TestCase (void)
253{
254}
255
256// TestHierarchyBuilder helpers
257
258void 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
277TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root)
278	: m_root(root)
279{
280	addChildGroupsToMap(m_groupMap, root);
281}
282
283TestHierarchyBuilder::~TestHierarchyBuilder (void)
284{
285}
286
287TestCase* 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
319static 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
328static 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
340static 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
352static 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
362static 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
374void 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
386void TestSet::addCase (const TestCase* testCase)
387{
388	addNodeAndParents(m_set, testCase);
389}
390
391void TestSet::addGroup (const TestGroup* testGroup)
392{
393	addNodeAndParents(m_set, testGroup);
394	addChildren(m_set, testGroup);
395}
396
397void 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
409void 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
418void 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
431ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root)
432	: m_root(root)
433{
434}
435
436ConstTestNodeIterator ConstTestNodeIterator::begin (const TestNode* root)
437{
438	ConstTestNodeIterator iter(root);
439	iter.m_iterStack.push_back(GroupState(DE_NULL));
440	return iter;
441}
442
443ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root)
444{
445	DE_UNREF(root);
446	return ConstTestNodeIterator(root);
447}
448
449ConstTestNodeIterator& 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
484ConstTestNodeIterator ConstTestNodeIterator::operator++ (int)
485{
486	ConstTestNodeIterator copy(*this);
487	++(*this);
488	return copy;
489}
490
491const 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
503bool ConstTestNodeIterator::operator!= (const ConstTestNodeIterator& other) const
504{
505	return m_root != other.m_root || m_iterStack != other.m_iterStack;
506}
507
508} // xe
509