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 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 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 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 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, 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 104 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 146 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 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 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 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 193 TestGroup* TestGroup::createGroup (const char* name, const char* description) 194 { 195 return new TestGroup(this, TESTNODETYPE_GROUP, name, description); 196 } 197 198 TestCase* TestGroup::createCase (TestCaseType caseType, const char* name, const char* description) 199 { 200 return TestCase::createAsChild(this, caseType, name, description); 201 } 202 203 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 234 TestRoot::TestRoot (void) 235 : TestGroup(DE_NULL, TESTNODETYPE_ROOT, "", "") 236 { 237 } 238 239 // TestCase 240 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 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 252 TestCase::~TestCase (void) 253 { 254 } 255 256 // TestHierarchyBuilder helpers 257 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 277 TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root) 278 : m_root(root) 279 { 280 addChildGroupsToMap(m_groupMap, root); 281 } 282 283 TestHierarchyBuilder::~TestHierarchyBuilder (void) 284 { 285 } 286 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 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 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 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 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 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 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 386 void TestSet::addCase (const TestCase* testCase) 387 { 388 addNodeAndParents(m_set, testCase); 389 } 390 391 void TestSet::addGroup (const TestGroup* testGroup) 392 { 393 addNodeAndParents(m_set, testGroup); 394 addChildren(m_set, testGroup); 395 } 396 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 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 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 431 ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root) 432 : m_root(root) 433 { 434 } 435 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 443 ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root) 444 { 445 DE_UNREF(root); 446 return ConstTestNodeIterator(root); 447 } 448 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 484 ConstTestNodeIterator ConstTestNodeIterator::operator++ (int) 485 { 486 ConstTestNodeIterator copy(*this); 487 ++(*this); 488 return copy; 489 } 490 491 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 503 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