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