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