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