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, 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 104void 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 146const 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 173TestNode* TestNode::find (const char* path) 174{ 175 return const_cast<TestNode*>(const_cast<const TestNode*>(this)->find(path)); 176} 177 178// TestGroup 179 180TestGroup::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 187TestGroup::~TestGroup (void) 188{ 189 for (std::vector<TestNode*>::iterator i = m_children.begin(); i != m_children.end(); i++) 190 delete *i; 191} 192 193TestGroup* TestGroup::createGroup (const char* name, const char* description) 194{ 195 return new TestGroup(this, TESTNODETYPE_GROUP, name, description); 196} 197 198TestCase* TestGroup::createCase (TestCaseType caseType, const char* name, const char* description) 199{ 200 return TestCase::createAsChild(this, caseType, name, description); 201} 202 203const 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 234TestRoot::TestRoot (void) 235 : TestGroup(DE_NULL, TESTNODETYPE_ROOT, "", "") 236{ 237} 238 239// TestCase 240 241TestCase* TestCase::createAsChild(TestGroup* parent, TestCaseType caseType, const char *name, const char *description) 242{ 243 return new TestCase(parent, caseType, name, description); 244} 245 246TestCase::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 252TestCase::~TestCase (void) 253{ 254} 255 256// TestHierarchyBuilder helpers 257 258void 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 277TestHierarchyBuilder::TestHierarchyBuilder (TestRoot* root) 278 : m_root(root) 279{ 280 addChildGroupsToMap(m_groupMap, root); 281} 282 283TestHierarchyBuilder::~TestHierarchyBuilder (void) 284{ 285} 286 287TestCase* 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 319static 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 328static 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 340static 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 352static 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 362static 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 374void 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 386void TestSet::addCase (const TestCase* testCase) 387{ 388 addNodeAndParents(m_set, testCase); 389} 390 391void TestSet::addGroup (const TestGroup* testGroup) 392{ 393 addNodeAndParents(m_set, testGroup); 394 addChildren(m_set, testGroup); 395} 396 397void 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 409void 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 418void 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 431ConstTestNodeIterator::ConstTestNodeIterator (const TestNode* root) 432 : m_root(root) 433{ 434} 435 436ConstTestNodeIterator ConstTestNodeIterator::begin (const TestNode* root) 437{ 438 ConstTestNodeIterator iter(root); 439 iter.m_iterStack.push_back(GroupState(DE_NULL)); 440 return iter; 441} 442 443ConstTestNodeIterator ConstTestNodeIterator::end (const TestNode* root) 444{ 445 DE_UNREF(root); 446 return ConstTestNodeIterator(root); 447} 448 449ConstTestNodeIterator& 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 484ConstTestNodeIterator ConstTestNodeIterator::operator++ (int) 485{ 486 ConstTestNodeIterator copy(*this); 487 ++(*this); 488 return copy; 489} 490 491const 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 503bool ConstTestNodeIterator::operator!= (const ConstTestNodeIterator& other) const 504{ 505 return m_root != other.m_root || m_iterStack != other.m_iterStack; 506} 507 508} // xe 509