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:
36 return "SelfValidate";
37 case TESTCASETYPE_CAPABILITY:
38 return "Capability";
39 case TESTCASETYPE_ACCURACY:
40 return "Accuracy";
41 case TESTCASETYPE_PERFORMANCE:
42 return "Performance";
43 default:
44 DE_ASSERT(false);
45 return DE_NULL;
46 }
47 }
48
getFirstComponentLength(const char * path)49 static inline int getFirstComponentLength(const char *path)
50 {
51 int compLen = 0;
52 while (path[compLen] != 0 && path[compLen] != '.')
53 compLen++;
54 return compLen;
55 }
56
compareNameToPathComponent(const char * name,const char * path,int compLen)57 static bool compareNameToPathComponent(const char *name, const char *path, int compLen)
58 {
59 for (int pos = 0; pos < compLen; pos++)
60 {
61 if (name[pos] != path[pos])
62 return false;
63 }
64
65 if (name[compLen] != 0)
66 return false;
67
68 return true;
69 }
70
splitPath(const char * path,std::vector<std::string> & components)71 static void splitPath(const char *path, std::vector<std::string> &components)
72 {
73 std::string pathStr(path);
74 int compStart = 0;
75
76 for (int pos = 0; pos < (int)pathStr.length(); pos++)
77 {
78 if (pathStr[pos] == '.')
79 {
80 components.push_back(pathStr.substr(compStart, pos - compStart));
81 compStart = pos + 1;
82 }
83 }
84
85 DE_ASSERT(compStart < (int)pathStr.length());
86 components.push_back(pathStr.substr(compStart));
87 }
88
89 // TestNode
90
TestNode(TestGroup * parent,TestNodeType nodeType,const char * name)91 TestNode::TestNode(TestGroup *parent, TestNodeType nodeType, const char *name)
92 : m_parent(parent)
93 , m_nodeType(nodeType)
94 , m_name(name)
95 {
96 if (m_parent)
97 {
98 // Verify that the name is unique.
99 if (parent->m_childNames.find(name) != parent->m_childNames.end())
100 throw Error(std::string("Duplicate node '") + name + "' in '" + parent->getFullPath());
101
102 m_parent->m_children.push_back(this);
103 m_parent->m_childNames.insert(name);
104 }
105 }
106
getFullPath(std::string & dst) const107 void TestNode::getFullPath(std::string &dst) const
108 {
109 dst.clear();
110
111 int nameLen = 0;
112 const TestNode *curNode = this;
113
114 for (;;)
115 {
116 nameLen += (int)curNode->m_name.length();
117
118 DE_ASSERT(curNode->m_parent);
119 if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT)
120 {
121 nameLen += 1;
122 curNode = curNode->m_parent;
123 }
124 else
125 break;
126 }
127
128 dst.resize(nameLen);
129
130 curNode = this;
131 int pos = nameLen;
132
133 for (;;)
134 {
135 std::copy(curNode->m_name.begin(), curNode->m_name.end(), dst.begin() + (pos - curNode->m_name.length()));
136 pos -= (int)curNode->m_name.length();
137
138 DE_ASSERT(curNode->m_parent);
139 if (curNode->m_parent->getNodeType() != TESTNODETYPE_ROOT)
140 {
141 dst[--pos] = '.';
142 curNode = curNode->m_parent;
143 }
144 else
145 break;
146 }
147 }
148
find(const char * path) const149 const TestNode *TestNode::find(const char *path) const
150 {
151 if (m_nodeType == TESTNODETYPE_ROOT)
152 {
153 // Don't need to consider current node.
154 return static_cast<const TestGroup *>(this)->findChildNode(path);
155 }
156 else
157 {
158 // Check if first component matches this node.
159 int compLen = getFirstComponentLength(path);
160 XE_CHECK(compLen > 0);
161
162 if (compareNameToPathComponent(getName(), path, compLen))
163 {
164 if (path[compLen] == 0)
165 return this;
166 else if (getNodeType() == TESTNODETYPE_GROUP)
167 return static_cast<const TestGroup *>(this)->findChildNode(path + compLen + 1);
168 else
169 return DE_NULL;
170 }
171 else
172 return DE_NULL;
173 }
174 }
175
find(const char * path)176 TestNode *TestNode::find(const char *path)
177 {
178 return const_cast<TestNode *>(const_cast<const TestNode *>(this)->find(path));
179 }
180
181 // TestGroup
182
TestGroup(TestGroup * parent,TestNodeType nodeType,const char * name)183 TestGroup::TestGroup(TestGroup *parent, TestNodeType nodeType, const char *name) : TestNode(parent, nodeType, name)
184 {
185 DE_ASSERT(nodeType == TESTNODETYPE_GROUP || nodeType == TESTNODETYPE_ROOT);
186 DE_ASSERT(!parent == (nodeType == TESTNODETYPE_ROOT));
187 }
188
~TestGroup(void)189 TestGroup::~TestGroup(void)
190 {
191 for (std::vector<TestNode *>::iterator i = m_children.begin(); i != m_children.end(); i++)
192 delete *i;
193 }
194
createGroup(const char * name)195 TestGroup *TestGroup::createGroup(const char *name)
196 {
197 return new TestGroup(this, TESTNODETYPE_GROUP, name);
198 }
199
createCase(TestCaseType caseType,const char * name)200 TestCase *TestGroup::createCase(TestCaseType caseType, const char *name)
201 {
202 return TestCase::createAsChild(this, caseType, name);
203 }
204
findChildNode(const char * path) const205 const TestNode *TestGroup::findChildNode(const char *path) const
206 {
207 int compLen = getFirstComponentLength(path);
208 XE_CHECK(compLen > 0);
209
210 // Try to find matching children.
211 const TestNode *matchingNode = DE_NULL;
212 for (vector<TestNode *>::const_iterator iter = m_children.begin(); iter != m_children.end(); iter++)
213 {
214 if (compareNameToPathComponent((*iter)->getName(), path, compLen))
215 {
216 matchingNode = *iter;
217 break;
218 }
219 }
220
221 if (matchingNode)
222 {
223 if (path[compLen] == 0)
224 return matchingNode; // Last element in path, return matching node.
225 else if (matchingNode->getNodeType() == TESTNODETYPE_GROUP)
226 return static_cast<const TestGroup *>(matchingNode)->findChildNode(path + compLen + 1);
227 else
228 return DE_NULL;
229 }
230 else
231 return DE_NULL;
232 }
233
234 // TestRoot
235
TestRoot(void)236 TestRoot::TestRoot(void) : TestGroup(DE_NULL, TESTNODETYPE_ROOT, "")
237 {
238 }
239
240 // TestCase
241
createAsChild(TestGroup * parent,TestCaseType caseType,const char * name)242 TestCase *TestCase::createAsChild(TestGroup *parent, TestCaseType caseType, const char *name)
243 {
244 return new TestCase(parent, caseType, name);
245 }
246
TestCase(TestGroup * parent,TestCaseType caseType,const char * name)247 TestCase::TestCase(TestGroup *parent, TestCaseType caseType, const char *name)
248 : TestNode(parent, TESTNODETYPE_TEST_CASE, name)
249 , m_caseType(caseType)
250 {
251 }
252
~TestCase(void)253 TestCase::~TestCase(void)
254 {
255 }
256
257 // TestHierarchyBuilder helpers
258
addChildGroupsToMap(std::map<std::string,TestGroup * > & groupMap,TestGroup * group)259 void addChildGroupsToMap(std::map<std::string, TestGroup *> &groupMap, TestGroup *group)
260 {
261 for (int ndx = 0; ndx < group->getNumChildren(); ndx++)
262 {
263 TestNode *node = group->getChild(ndx);
264 if (node->getNodeType() == TESTNODETYPE_GROUP)
265 {
266 TestGroup *childGroup = static_cast<TestGroup *>(node);
267 std::string fullPath;
268 childGroup->getFullPath(fullPath);
269
270 groupMap.insert(std::make_pair(fullPath, childGroup));
271 addChildGroupsToMap(groupMap, childGroup);
272 }
273 }
274 }
275
276 // TestHierarchyBuilder
277
TestHierarchyBuilder(TestRoot * root)278 TestHierarchyBuilder::TestHierarchyBuilder(TestRoot *root) : 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());
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());
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 || 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 || node->getNodeType() == TESTNODETYPE_ROOT);
403 removeGroup(static_cast<const TestGroup *>(node));
404 }
405 }
406
removeCase(const TestCase * testCase)407 void TestSet::removeCase(const TestCase *testCase)
408 {
409 if (m_set.find(testCase) != m_set.end())
410 {
411 m_set.erase(testCase);
412 removeEmptyGroups(m_set, testCase->getParent());
413 }
414 }
415
removeGroup(const TestGroup * testGroup)416 void TestSet::removeGroup(const TestGroup *testGroup)
417 {
418 if (m_set.find(testGroup) != m_set.end())
419 {
420 m_set.erase(testGroup);
421 removeChildren(m_set, testGroup);
422 if (testGroup->getParent() != DE_NULL)
423 removeEmptyGroups(m_set, testGroup->getParent());
424 }
425 }
426
427 // ConstTestNodeIterator
428
ConstTestNodeIterator(const TestNode * root)429 ConstTestNodeIterator::ConstTestNodeIterator(const TestNode *root) : m_root(root)
430 {
431 }
432
begin(const TestNode * root)433 ConstTestNodeIterator ConstTestNodeIterator::begin(const TestNode *root)
434 {
435 ConstTestNodeIterator iter(root);
436 iter.m_iterStack.push_back(GroupState(DE_NULL));
437 return iter;
438 }
439
end(const TestNode * root)440 ConstTestNodeIterator ConstTestNodeIterator::end(const TestNode *root)
441 {
442 DE_UNREF(root);
443 return ConstTestNodeIterator(root);
444 }
445
operator ++(void)446 ConstTestNodeIterator &ConstTestNodeIterator::operator++(void)
447 {
448 DE_ASSERT(!m_iterStack.empty());
449
450 const TestNode *curNode = **this;
451 TestNodeType curNodeType = curNode->getNodeType();
452
453 if ((curNodeType == TESTNODETYPE_GROUP || curNodeType == TESTNODETYPE_ROOT) &&
454 static_cast<const TestGroup *>(curNode)->getNumChildren() > 0)
455 {
456 m_iterStack.push_back(GroupState(static_cast<const TestGroup *>(curNode)));
457 }
458 else
459 {
460 for (;;)
461 {
462 const TestGroup *group = m_iterStack.back().group;
463 int &childNdx = m_iterStack.back().childNdx;
464 int numChildren = group ? group->getNumChildren() : 1;
465
466 childNdx += 1;
467 if (childNdx == numChildren)
468 {
469 m_iterStack.pop_back();
470 if (m_iterStack.empty())
471 break;
472 }
473 else
474 break;
475 }
476 }
477
478 return *this;
479 }
480
operator ++(int)481 ConstTestNodeIterator ConstTestNodeIterator::operator++(int)
482 {
483 ConstTestNodeIterator copy(*this);
484 ++(*this);
485 return copy;
486 }
487
operator *(void) const488 const TestNode *ConstTestNodeIterator::operator*(void) const
489 {
490 DE_ASSERT(!m_iterStack.empty());
491 if (m_iterStack.size() == 1)
492 {
493 DE_ASSERT(m_iterStack[0].group == DE_NULL && m_iterStack[0].childNdx == 0);
494 return m_root;
495 }
496 else
497 return m_iterStack.back().group->getChild(m_iterStack.back().childNdx);
498 }
499
operator !=(const ConstTestNodeIterator & other) const500 bool ConstTestNodeIterator::operator!=(const ConstTestNodeIterator &other) const
501 {
502 return m_root != other.m_root || m_iterStack != other.m_iterStack;
503 }
504
505 } // namespace xe
506