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 result parser. 22 *//*--------------------------------------------------------------------*/ 23 24#include "xeTestResultParser.hpp" 25#include "xeTestCaseResult.hpp" 26#include "xeBatchResult.hpp" 27#include "deString.h" 28#include "deInt32.h" 29 30#include <sstream> 31#include <stdlib.h> 32 33using std::string; 34using std::vector; 35 36namespace xe 37{ 38 39static inline int toInt (const char* str) 40{ 41 return atoi(str); 42} 43 44static inline double toDouble (const char* str) 45{ 46 return atof(str); 47} 48 49static inline deInt64 toInt64 (const char* str) 50{ 51 std::istringstream s (str); 52 deInt64 val; 53 54 s >> val; 55 56 return val; 57} 58 59static inline bool toBool (const char* str) 60{ 61 return deStringEqual(str, "OK") || deStringEqual(str, "True"); 62} 63 64static const char* stripLeadingWhitespace (const char* str) 65{ 66 int whitespaceCount = 0; 67 68 while (str[whitespaceCount] != 0 && 69 (str[whitespaceCount] == ' ' || 70 str[whitespaceCount] == '\t' || 71 str[whitespaceCount] == '\r' || 72 str[whitespaceCount] == '\n')) 73 whitespaceCount += 1; 74 75 return str + whitespaceCount; 76} 77 78struct EnumMapEntry 79{ 80 deUint32 hash; 81 const char* name; 82 int value; 83}; 84 85static const EnumMapEntry s_statusCodeMap[] = 86{ 87 { 0x7c8a99bc, "Pass", TESTSTATUSCODE_PASS }, 88 { 0x7c851ca1, "Fail", TESTSTATUSCODE_FAIL }, 89 { 0x10ecd324, "QualityWarning", TESTSTATUSCODE_QUALITY_WARNING }, 90 { 0x341ae835, "CompatibilityWarning", TESTSTATUSCODE_COMPATIBILITY_WARNING }, 91 { 0x058acbca, "Pending", TESTSTATUSCODE_PENDING }, 92 { 0xc4d74b26, "Running", TESTSTATUSCODE_RUNNING }, 93 { 0x6409f93c, "NotSupported", TESTSTATUSCODE_NOT_SUPPORTED }, 94 { 0xfa5a9ab7, "ResourceError", TESTSTATUSCODE_RESOURCE_ERROR }, 95 { 0xad6793ec, "InternalError", TESTSTATUSCODE_INTERNAL_ERROR }, 96 { 0x838f3034, "Canceled", TESTSTATUSCODE_CANCELED }, 97 { 0x42b6efac, "Timeout", TESTSTATUSCODE_TIMEOUT }, 98 { 0x0cfb98f6, "Crash", TESTSTATUSCODE_CRASH }, 99 { 0xe326e01d, "Disabled", TESTSTATUSCODE_DISABLED }, 100 { 0x77061af2, "Terminated", TESTSTATUSCODE_TERMINATED } 101}; 102 103static const EnumMapEntry s_resultItemMap[] = 104{ 105 { 0xce8ac2e4, "Result", ri::TYPE_RESULT }, 106 { 0x7c8cdcea, "Text", ri::TYPE_TEXT }, 107 { 0xc6540c6e, "Number", ri::TYPE_NUMBER }, 108 { 0x0d656c88, "Image", ri::TYPE_IMAGE }, 109 { 0x8ac9ee14, "ImageSet", ri::TYPE_IMAGESET }, 110 { 0x1181fa5a, "VertexShader", ri::TYPE_SHADER }, 111 { 0xa93daef0, "FragmentShader", ri::TYPE_SHADER }, 112 { 0x8f066128, "GeometryShader", ri::TYPE_SHADER }, 113 { 0x235a931c, "TessControlShader", ri::TYPE_SHADER }, 114 { 0xa1bf7153, "TessEvaluationShader", ri::TYPE_SHADER }, 115 { 0x6c1415d9, "ComputeShader", ri::TYPE_SHADER }, 116 { 0x72863a54, "ShaderProgram", ri::TYPE_SHADERPROGRAM }, 117 { 0xb4efc08d, "ShaderSource", ri::TYPE_SHADERSOURCE }, 118 { 0xaee4380a, "SpirVAssemblySource", ri::TYPE_SPIRVSOURCE }, 119 { 0xff265913, "InfoLog", ri::TYPE_INFOLOG }, 120 { 0x84159b73, "EglConfig", ri::TYPE_EGLCONFIG }, 121 { 0xdd34391f, "EglConfigSet", ri::TYPE_EGLCONFIGSET }, 122 { 0xebbb3aba, "Section", ri::TYPE_SECTION }, 123 { 0xa0f15677, "KernelSource", ri::TYPE_KERNELSOURCE }, 124 { 0x1ee9083a, "CompileInfo", ri::TYPE_COMPILEINFO }, 125 { 0xf1004023, "SampleList", ri::TYPE_SAMPLELIST }, 126 { 0xf0feae93, "SampleInfo", ri::TYPE_SAMPLEINFO }, 127 { 0x2aa6f14e, "ValueInfo", ri::TYPE_VALUEINFO }, 128 { 0xd09429e7, "Sample", ri::TYPE_SAMPLE }, 129 { 0x0e4a4722, "Value", ri::TYPE_SAMPLEVALUE }, 130}; 131 132static const EnumMapEntry s_imageFormatMap[] = 133{ 134 { 0xcc4ffac8, "RGB888", ri::Image::FORMAT_RGB888 }, 135 { 0x20dcb0c1, "RGBA8888", ri::Image::FORMAT_RGBA8888 } 136}; 137 138static const EnumMapEntry s_compressionMap[] = 139{ 140 { 0x7c89bbd5, "None", ri::Image::COMPRESSION_NONE }, 141 { 0x0b88118a, "PNG", ri::Image::COMPRESSION_PNG } 142}; 143 144static const EnumMapEntry s_shaderTypeFromTagMap[] = 145{ 146 { 0x1181fa5a, "VertexShader", ri::Shader::SHADERTYPE_VERTEX }, 147 { 0xa93daef0, "FragmentShader", ri::Shader::SHADERTYPE_FRAGMENT }, 148 { 0x8f066128, "GeometryShader", ri::Shader::SHADERTYPE_GEOMETRY }, 149 { 0x235a931c, "TessControlShader", ri::Shader::SHADERTYPE_TESS_CONTROL }, 150 { 0xa1bf7153, "TessEvaluationShader", ri::Shader::SHADERTYPE_TESS_EVALUATION }, 151 { 0x6c1415d9, "ComputeShader", ri::Shader::SHADERTYPE_COMPUTE }, 152}; 153 154static const EnumMapEntry s_testTypeMap[] = 155{ 156 { 0x7fa80959, "SelfValidate", TESTCASETYPE_SELF_VALIDATE }, 157 { 0xdb797567, "Capability", TESTCASETYPE_CAPABILITY }, 158 { 0x2ca3ec10, "Accuracy", TESTCASETYPE_ACCURACY }, 159 { 0xa48ac277, "Performance", TESTCASETYPE_PERFORMANCE } 160}; 161 162static const EnumMapEntry s_logVersionMap[] = 163{ 164 { 0x0b7dac93, "0.2.0", TESTLOGVERSION_0_2_0 }, 165 { 0x0b7db0d4, "0.3.0", TESTLOGVERSION_0_3_0 }, 166 { 0x0b7db0d5, "0.3.1", TESTLOGVERSION_0_3_1 }, 167 { 0x0b7db0d6, "0.3.2", TESTLOGVERSION_0_3_2 }, 168 { 0x0b7db0d7, "0.3.3", TESTLOGVERSION_0_3_3 }, 169 { 0x0b7db0d8, "0.3.4", TESTLOGVERSION_0_3_4 } 170}; 171 172static const EnumMapEntry s_sampleValueTagMap[] = 173{ 174 { 0xddf2d0d1, "Predictor", ri::ValueInfo::VALUETAG_PREDICTOR }, 175 { 0x9bee2c34, "Response", ri::ValueInfo::VALUETAG_RESPONSE }, 176}; 177 178#if defined(DE_DEBUG) 179static void printHashes (const char* name, const EnumMapEntry* entries, int numEntries) 180{ 181 printf("%s:\n", name); 182 183 for (int ndx = 0; ndx < numEntries; ndx++) 184 printf("0x%08x\t%s\n", deStringHash(entries[ndx].name), entries[ndx].name); 185 186 printf("\n"); 187} 188 189#define PRINT_HASHES(MAP) printHashes(#MAP, MAP, DE_LENGTH_OF_ARRAY(MAP)) 190 191void TestResultParser_printHashes (void) 192{ 193 PRINT_HASHES(s_statusCodeMap); 194 PRINT_HASHES(s_resultItemMap); 195 PRINT_HASHES(s_imageFormatMap); 196 PRINT_HASHES(s_compressionMap); 197 PRINT_HASHES(s_shaderTypeFromTagMap); 198 PRINT_HASHES(s_testTypeMap); 199 PRINT_HASHES(s_logVersionMap); 200 PRINT_HASHES(s_sampleValueTagMap); 201} 202#endif 203 204static inline int getEnumValue (const char* enumName, const EnumMapEntry* entries, int numEntries, const char* name) 205{ 206 deUint32 hash = deStringHash(name); 207 208 for (int ndx = 0; ndx < numEntries; ndx++) 209 { 210 if (entries[ndx].hash == hash && deStringEqual(entries[ndx].name, name)) 211 return entries[ndx].value; 212 } 213 214 throw TestResultParseError(string("Could not map '") + name + "' to " + enumName); 215} 216 217TestStatusCode getTestStatusCode (const char* statusCode) 218{ 219 return (TestStatusCode)getEnumValue("status code", s_statusCodeMap, DE_LENGTH_OF_ARRAY(s_statusCodeMap), statusCode); 220} 221 222static ri::Type getResultItemType (const char* elemName) 223{ 224 return (ri::Type)getEnumValue("result item type", s_resultItemMap, DE_LENGTH_OF_ARRAY(s_resultItemMap), elemName); 225} 226 227static ri::Image::Format getImageFormat (const char* imageFormat) 228{ 229 return (ri::Image::Format)getEnumValue("image format", s_imageFormatMap, DE_LENGTH_OF_ARRAY(s_imageFormatMap), imageFormat); 230} 231 232static ri::Image::Compression getImageCompression (const char* compression) 233{ 234 return (ri::Image::Compression)getEnumValue("image compression", s_compressionMap, DE_LENGTH_OF_ARRAY(s_compressionMap), compression); 235} 236 237static ri::Shader::ShaderType getShaderTypeFromTagName (const char* shaderType) 238{ 239 return (ri::Shader::ShaderType)getEnumValue("shader type", s_shaderTypeFromTagMap, DE_LENGTH_OF_ARRAY(s_shaderTypeFromTagMap), shaderType); 240} 241 242static TestCaseType getTestCaseType (const char* caseType) 243{ 244 return (TestCaseType)getEnumValue("test case type", s_testTypeMap, DE_LENGTH_OF_ARRAY(s_testTypeMap), caseType); 245} 246 247static TestLogVersion getTestLogVersion (const char* logVersion) 248{ 249 return (TestLogVersion)getEnumValue("test log version", s_logVersionMap, DE_LENGTH_OF_ARRAY(s_logVersionMap), logVersion); 250} 251 252static ri::ValueInfo::ValueTag getSampleValueTag (const char* tag) 253{ 254 return (ri::ValueInfo::ValueTag)getEnumValue("sample value tag", s_sampleValueTagMap, DE_LENGTH_OF_ARRAY(s_sampleValueTagMap), tag); 255} 256 257static TestCaseType getTestCaseTypeFromPath (const char* casePath) 258{ 259 if (deStringBeginsWith(casePath, "dEQP-GLES2.")) 260 { 261 const char* group = casePath+11; 262 if (deStringBeginsWith(group, "capability.")) 263 return TESTCASETYPE_CAPABILITY; 264 else if (deStringBeginsWith(group, "accuracy.")) 265 return TESTCASETYPE_ACCURACY; 266 else if (deStringBeginsWith(group, "performance.")) 267 return TESTCASETYPE_PERFORMANCE; 268 } 269 270 return TESTCASETYPE_SELF_VALIDATE; 271} 272 273static ri::NumericValue getNumericValue (const std::string& value) 274{ 275 const bool isFloat = value.find('.') != std::string::npos || value.find('e') != std::string::npos; 276 277 if (isFloat) 278 { 279 const double num = toDouble(stripLeadingWhitespace(value.c_str())); 280 return ri::NumericValue(num); 281 } 282 else 283 { 284 const deInt64 num = toInt64(stripLeadingWhitespace(value.c_str())); 285 return ri::NumericValue(num); 286 } 287} 288 289TestResultParser::TestResultParser (void) 290 : m_result (DE_NULL) 291 , m_state (STATE_NOT_INITIALIZED) 292 , m_logVersion (TESTLOGVERSION_LAST) 293 , m_curItemList (DE_NULL) 294 , m_base64DecodeOffset (0) 295{ 296} 297 298TestResultParser::~TestResultParser (void) 299{ 300} 301 302void TestResultParser::clear (void) 303{ 304 m_xmlParser.clear(); 305 m_itemStack.clear(); 306 307 m_result = DE_NULL; 308 m_state = STATE_NOT_INITIALIZED; 309 m_logVersion = TESTLOGVERSION_LAST; 310 m_curItemList = DE_NULL; 311 m_base64DecodeOffset = 0; 312 m_curNumValue.clear(); 313} 314 315void TestResultParser::init (TestCaseResult* dstResult) 316{ 317 clear(); 318 m_result = dstResult; 319 m_state = STATE_INITIALIZED; 320 m_curItemList = &dstResult->resultItems; 321} 322 323TestResultParser::ParseResult TestResultParser::parse (const deUint8* bytes, int numBytes) 324{ 325 DE_ASSERT(m_result && m_state != STATE_NOT_INITIALIZED); 326 327 try 328 { 329 bool resultChanged = false; 330 331 m_xmlParser.feed(bytes, numBytes); 332 333 for (;;) 334 { 335 xml::Element curElement = m_xmlParser.getElement(); 336 337 if (curElement == xml::ELEMENT_INCOMPLETE || 338 curElement == xml::ELEMENT_END_OF_STRING) 339 break; 340 341 switch (curElement) 342 { 343 case xml::ELEMENT_START: handleElementStart(); break; 344 case xml::ELEMENT_END: handleElementEnd(); break; 345 case xml::ELEMENT_DATA: handleData(); break; 346 347 default: 348 DE_ASSERT(false); 349 } 350 351 resultChanged = true; 352 m_xmlParser.advance(); 353 } 354 355 if (m_xmlParser.getElement() == xml::ELEMENT_END_OF_STRING) 356 { 357 if (m_state != STATE_TEST_CASE_RESULT_ENDED) 358 throw TestResultParseError("Unexpected end of log data"); 359 360 return PARSERESULT_COMPLETE; 361 } 362 else 363 return resultChanged ? PARSERESULT_CHANGED 364 : PARSERESULT_NOT_CHANGED; 365 } 366 catch (const TestResultParseError& e) 367 { 368 // Set error code to result. 369 m_result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR; 370 m_result->statusDetails = e.what(); 371 372 return PARSERESULT_ERROR; 373 } 374 catch (const xml::ParseError& e) 375 { 376 // Set error code to result. 377 m_result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR; 378 m_result->statusDetails = e.what(); 379 380 return PARSERESULT_ERROR; 381 } 382} 383 384const char* TestResultParser::getAttribute (const char* name) 385{ 386 if (!m_xmlParser.hasAttribute(name)) 387 throw TestResultParseError(string("Missing attribute '") + name + "' in <" + m_xmlParser.getElementName() + ">"); 388 389 return m_xmlParser.getAttribute(name); 390} 391 392ri::Item* TestResultParser::getCurrentItem (void) 393{ 394 return !m_itemStack.empty() ? m_itemStack.back() : DE_NULL; 395} 396 397ri::List* TestResultParser::getCurrentItemList (void) 398{ 399 DE_ASSERT(m_curItemList); 400 return m_curItemList; 401} 402 403void TestResultParser::updateCurrentItemList (void) 404{ 405 m_curItemList = DE_NULL; 406 407 for (vector<ri::Item*>::reverse_iterator i = m_itemStack.rbegin(); i != m_itemStack.rend(); i++) 408 { 409 ri::Item* item = *i; 410 ri::Type type = item->getType(); 411 412 if (type == ri::TYPE_IMAGESET) 413 m_curItemList = &static_cast<ri::ImageSet*>(item)->images; 414 else if (type == ri::TYPE_SECTION) 415 m_curItemList = &static_cast<ri::Section*>(item)->items; 416 else if (type == ri::TYPE_EGLCONFIGSET) 417 m_curItemList = &static_cast<ri::EglConfigSet*>(item)->configs; 418 else if (type == ri::TYPE_SHADERPROGRAM) 419 m_curItemList = &static_cast<ri::ShaderProgram*>(item)->shaders; 420 421 if (m_curItemList) 422 break; 423 } 424 425 if (!m_curItemList) 426 m_curItemList = &m_result->resultItems; 427} 428 429void TestResultParser::pushItem (ri::Item* item) 430{ 431 m_itemStack.push_back(item); 432 updateCurrentItemList(); 433} 434 435void TestResultParser::popItem (void) 436{ 437 m_itemStack.pop_back(); 438 updateCurrentItemList(); 439} 440 441void TestResultParser::handleElementStart (void) 442{ 443 const char* elemName = m_xmlParser.getElementName(); 444 445 if (m_state == STATE_INITIALIZED) 446 { 447 // Expect TestCaseResult. 448 if (!deStringEqual(elemName, "TestCaseResult")) 449 throw TestResultParseError(string("Expected <TestCaseResult>, got <") + elemName + ">"); 450 451 const char* version = getAttribute("Version"); 452 m_logVersion = getTestLogVersion(version); 453 // \note Currently assumed that all known log versions are supported. 454 455 m_result->casePath = getAttribute("CasePath"); 456 m_result->caseType = TESTCASETYPE_SELF_VALIDATE; 457 458 if (m_xmlParser.hasAttribute("CaseType")) 459 m_result->caseType = getTestCaseType(m_xmlParser.getAttribute("CaseType")); 460 else 461 { 462 // Do guess based on path for legacy log files. 463 if (m_logVersion >= TESTLOGVERSION_0_3_2) 464 throw TestResultParseError("Missing CaseType attribute in <TestCaseResult>"); 465 m_result->caseType = getTestCaseTypeFromPath(m_result->casePath.c_str()); 466 } 467 468 m_state = STATE_IN_TEST_CASE_RESULT; 469 } 470 else 471 { 472 ri::List* curList = getCurrentItemList(); 473 ri::Type itemType = getResultItemType(elemName); 474 ri::Item* item = DE_NULL; 475 ri::Item* parentItem = getCurrentItem(); 476 ri::Type parentType = parentItem ? parentItem->getType() : ri::TYPE_LAST; 477 478 switch (itemType) 479 { 480 case ri::TYPE_RESULT: 481 { 482 ri::Result* result = curList->allocItem<ri::Result>(); 483 result->statusCode = getTestStatusCode(getAttribute("StatusCode")); 484 item = result; 485 break; 486 } 487 488 case ri::TYPE_TEXT: 489 item = curList->allocItem<ri::Text>(); 490 break; 491 492 case ri::TYPE_SECTION: 493 { 494 ri::Section* section = curList->allocItem<ri::Section>(); 495 section->name = getAttribute("Name"); 496 section->description = getAttribute("Description"); 497 item = section; 498 break; 499 } 500 501 case ri::TYPE_NUMBER: 502 { 503 ri::Number* number = curList->allocItem<ri::Number>(); 504 number->name = getAttribute("Name"); 505 number->description = getAttribute("Description"); 506 number->unit = getAttribute("Unit"); 507 508 if (m_xmlParser.hasAttribute("Tag")) 509 number->tag = m_xmlParser.getAttribute("Tag"); 510 511 item = number; 512 513 m_curNumValue.clear(); 514 break; 515 } 516 517 case ri::TYPE_IMAGESET: 518 { 519 ri::ImageSet* imageSet = curList->allocItem<ri::ImageSet>(); 520 imageSet->name = getAttribute("Name"); 521 imageSet->description = getAttribute("Description"); 522 item = imageSet; 523 break; 524 } 525 526 case ri::TYPE_IMAGE: 527 { 528 ri::Image* image = curList->allocItem<ri::Image>(); 529 image->name = getAttribute("Name"); 530 image->description = getAttribute("Description"); 531 image->width = toInt(getAttribute("Width")); 532 image->height = toInt(getAttribute("Height")); 533 image->format = getImageFormat(getAttribute("Format")); 534 image->compression = getImageCompression(getAttribute("CompressionMode")); 535 item = image; 536 break; 537 } 538 539 case ri::TYPE_SHADERPROGRAM: 540 { 541 ri::ShaderProgram* shaderProgram = curList->allocItem<ri::ShaderProgram>(); 542 shaderProgram->linkStatus = toBool(getAttribute("LinkStatus")); 543 item = shaderProgram; 544 break; 545 } 546 547 case ri::TYPE_SHADER: 548 { 549 if (parentType != ri::TYPE_SHADERPROGRAM) 550 throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>"); 551 552 ri::Shader* shader = curList->allocItem<ri::Shader>(); 553 554 shader->shaderType = getShaderTypeFromTagName(elemName); 555 shader->compileStatus = toBool(getAttribute("CompileStatus")); 556 557 item = shader; 558 break; 559 } 560 561 case ri::TYPE_SPIRVSOURCE: 562 { 563 if (parentType != ri::TYPE_SHADERPROGRAM) 564 throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>"); 565 item = curList->allocItem<ri::SpirVSource>(); 566 break; 567 } 568 569 case ri::TYPE_SHADERSOURCE: 570 if (parentType == ri::TYPE_SHADER) 571 item = &static_cast<ri::Shader*>(parentItem)->source; 572 else 573 throw TestResultParseError("Unexpected <ShaderSource>"); 574 break; 575 576 case ri::TYPE_INFOLOG: 577 if (parentType == ri::TYPE_SHADERPROGRAM) 578 item = &static_cast<ri::ShaderProgram*>(parentItem)->linkInfoLog; 579 else if (parentType == ri::TYPE_SHADER) 580 item = &static_cast<ri::Shader*>(parentItem)->infoLog; 581 else if (parentType == ri::TYPE_COMPILEINFO) 582 item = &static_cast<ri::CompileInfo*>(parentItem)->infoLog; 583 else 584 throw TestResultParseError("Unexpected <InfoLog>"); 585 break; 586 587 case ri::TYPE_KERNELSOURCE: 588 item = curList->allocItem<ri::KernelSource>(); 589 break; 590 591 case ri::TYPE_COMPILEINFO: 592 { 593 ri::CompileInfo* info = curList->allocItem<ri::CompileInfo>(); 594 info->name = getAttribute("Name"); 595 info->description = getAttribute("Description"); 596 info->compileStatus = toBool(getAttribute("CompileStatus")); 597 item = info; 598 break; 599 } 600 601 case ri::TYPE_EGLCONFIGSET: 602 { 603 ri::EglConfigSet* set = curList->allocItem<ri::EglConfigSet>(); 604 set->name = getAttribute("Name"); 605 set->description = m_xmlParser.hasAttribute("Description") ? m_xmlParser.getAttribute("Description") : ""; 606 item = set; 607 break; 608 } 609 610 case ri::TYPE_EGLCONFIG: 611 { 612 ri::EglConfig* config = curList->allocItem<ri::EglConfig>(); 613 config->bufferSize = toInt(getAttribute("BufferSize")); 614 config->redSize = toInt(getAttribute("RedSize")); 615 config->greenSize = toInt(getAttribute("GreenSize")); 616 config->blueSize = toInt(getAttribute("BlueSize")); 617 config->luminanceSize = toInt(getAttribute("LuminanceSize")); 618 config->alphaSize = toInt(getAttribute("AlphaSize")); 619 config->alphaMaskSize = toInt(getAttribute("AlphaMaskSize")); 620 config->bindToTextureRGB = toBool(getAttribute("BindToTextureRGB")); 621 config->bindToTextureRGBA = toBool(getAttribute("BindToTextureRGBA")); 622 config->colorBufferType = getAttribute("ColorBufferType"); 623 config->configCaveat = getAttribute("ConfigCaveat"); 624 config->configID = toInt(getAttribute("ConfigID")); 625 config->conformant = getAttribute("Conformant"); 626 config->depthSize = toInt(getAttribute("DepthSize")); 627 config->level = toInt(getAttribute("Level")); 628 config->maxPBufferWidth = toInt(getAttribute("MaxPBufferWidth")); 629 config->maxPBufferHeight = toInt(getAttribute("MaxPBufferHeight")); 630 config->maxPBufferPixels = toInt(getAttribute("MaxPBufferPixels")); 631 config->maxSwapInterval = toInt(getAttribute("MaxSwapInterval")); 632 config->minSwapInterval = toInt(getAttribute("MinSwapInterval")); 633 config->nativeRenderable = toBool(getAttribute("NativeRenderable")); 634 config->renderableType = getAttribute("RenderableType"); 635 config->sampleBuffers = toInt(getAttribute("SampleBuffers")); 636 config->samples = toInt(getAttribute("Samples")); 637 config->stencilSize = toInt(getAttribute("StencilSize")); 638 config->surfaceTypes = getAttribute("SurfaceTypes"); 639 config->transparentType = getAttribute("TransparentType"); 640 config->transparentRedValue = toInt(getAttribute("TransparentRedValue")); 641 config->transparentGreenValue = toInt(getAttribute("TransparentGreenValue")); 642 config->transparentBlueValue = toInt(getAttribute("TransparentBlueValue")); 643 item = config; 644 break; 645 } 646 647 case ri::TYPE_SAMPLELIST: 648 { 649 ri::SampleList* list = curList->allocItem<ri::SampleList>(); 650 list->name = getAttribute("Name"); 651 list->description = getAttribute("Description"); 652 item = list; 653 break; 654 } 655 656 case ri::TYPE_SAMPLEINFO: 657 { 658 if (parentType != ri::TYPE_SAMPLELIST) 659 throw TestResultParseError("<SampleInfo> outside of <SampleList>"); 660 661 ri::SampleList* list = static_cast<ri::SampleList*>(parentItem); 662 ri::SampleInfo* info = &list->sampleInfo; 663 664 item = info; 665 break; 666 } 667 668 case ri::TYPE_VALUEINFO: 669 { 670 if (parentType != ri::TYPE_SAMPLEINFO) 671 throw TestResultParseError("<ValueInfo> outside of <SampleInfo>"); 672 673 ri::SampleInfo* sampleInfo = static_cast<ri::SampleInfo*>(parentItem); 674 ri::ValueInfo* valueInfo = sampleInfo->valueInfos.allocItem<ri::ValueInfo>(); 675 676 valueInfo->name = getAttribute("Name"); 677 valueInfo->description = getAttribute("Description"); 678 valueInfo->tag = getSampleValueTag(getAttribute("Tag")); 679 680 if (m_xmlParser.hasAttribute("Unit")) 681 valueInfo->unit = getAttribute("Unit"); 682 683 item = valueInfo; 684 break; 685 } 686 687 case ri::TYPE_SAMPLE: 688 { 689 if (parentType != ri::TYPE_SAMPLELIST) 690 throw TestResultParseError("<Sample> outside of <SampleList>"); 691 692 ri::SampleList* list = static_cast<ri::SampleList*>(parentItem); 693 ri::Sample* sample = list->samples.allocItem<ri::Sample>(); 694 695 item = sample; 696 break; 697 } 698 699 case ri::TYPE_SAMPLEVALUE: 700 { 701 if (parentType != ri::TYPE_SAMPLE) 702 throw TestResultParseError("<Value> outside of <Sample>"); 703 704 ri::Sample* sample = static_cast<ri::Sample*>(parentItem); 705 ri::SampleValue* value = sample->values.allocItem<ri::SampleValue>(); 706 707 item = value; 708 break; 709 } 710 711 default: 712 throw TestResultParseError(string("Unsupported element '") + elemName + ("'")); 713 } 714 715 DE_ASSERT(item); 716 pushItem(item); 717 718 // Reset base64 decoding offset. 719 m_base64DecodeOffset = 0; 720 } 721} 722 723void TestResultParser::handleElementEnd (void) 724{ 725 const char* elemName = m_xmlParser.getElementName(); 726 727 if (m_state != STATE_IN_TEST_CASE_RESULT) 728 throw TestResultParseError(string("Unexpected </") + elemName + "> outside of <TestCaseResult>"); 729 730 if (deStringEqual(elemName, "TestCaseResult")) 731 { 732 // Logs from buggy test cases may contain invalid XML. 733 // DE_ASSERT(getCurrentItem() == DE_NULL); 734 // \todo [2012-11-22 pyry] Log warning. 735 736 m_state = STATE_TEST_CASE_RESULT_ENDED; 737 } 738 else 739 { 740 ri::Type itemType = getResultItemType(elemName); 741 ri::Item* curItem = getCurrentItem(); 742 743 if (!curItem || itemType != curItem->getType()) 744 throw TestResultParseError(string("Unexpected </") + elemName + ">"); 745 746 if (itemType == ri::TYPE_RESULT) 747 { 748 ri::Result* result = static_cast<ri::Result*>(curItem); 749 m_result->statusCode = result->statusCode; 750 m_result->statusDetails = result->details; 751 } 752 else if (itemType == ri::TYPE_NUMBER) 753 { 754 // Parse value for number. 755 ri::Number* number = static_cast<ri::Number*>(curItem); 756 number->value = getNumericValue(m_curNumValue); 757 m_curNumValue.clear(); 758 } 759 else if (itemType == ri::TYPE_SAMPLEVALUE) 760 { 761 ri::SampleValue* value = static_cast<ri::SampleValue*>(curItem); 762 value->value = getNumericValue(m_curNumValue); 763 m_curNumValue.clear(); 764 } 765 766 popItem(); 767 } 768} 769 770void TestResultParser::handleData (void) 771{ 772 ri::Item* curItem = getCurrentItem(); 773 ri::Type type = curItem ? curItem->getType() : ri::TYPE_LAST; 774 775 switch (type) 776 { 777 case ri::TYPE_RESULT: 778 m_xmlParser.appendDataStr(static_cast<ri::Result*>(curItem)->details); 779 break; 780 781 case ri::TYPE_TEXT: 782 m_xmlParser.appendDataStr(static_cast<ri::Text*>(curItem)->text); 783 break; 784 785 case ri::TYPE_SHADERSOURCE: 786 m_xmlParser.appendDataStr(static_cast<ri::ShaderSource*>(curItem)->source); 787 break; 788 789 case ri::TYPE_SPIRVSOURCE: 790 m_xmlParser.appendDataStr(static_cast<ri::SpirVSource*>(curItem)->source); 791 break; 792 793 case ri::TYPE_INFOLOG: 794 m_xmlParser.appendDataStr(static_cast<ri::InfoLog*>(curItem)->log); 795 break; 796 797 case ri::TYPE_KERNELSOURCE: 798 m_xmlParser.appendDataStr(static_cast<ri::KernelSource*>(curItem)->source); 799 break; 800 801 case ri::TYPE_NUMBER: 802 case ri::TYPE_SAMPLEVALUE: 803 m_xmlParser.appendDataStr(m_curNumValue); 804 break; 805 806 case ri::TYPE_IMAGE: 807 { 808 ri::Image* image = static_cast<ri::Image*>(curItem); 809 810 // Base64 decode. 811 int numBytesIn = m_xmlParser.getDataSize(); 812 813 for (int inNdx = 0; inNdx < numBytesIn; inNdx++) 814 { 815 deUint8 byte = m_xmlParser.getDataByte(inNdx); 816 deUint8 decodedBits = 0; 817 818 if (de::inRange<deInt8>(byte, 'A', 'Z')) 819 decodedBits = (deUint8)(byte - 'A'); 820 else if (de::inRange<deInt8>(byte, 'a', 'z')) 821 decodedBits = (deUint8)(('Z'-'A'+1) + (byte-'a')); 822 else if (de::inRange<deInt8>(byte, '0', '9')) 823 decodedBits = (deUint8)(('Z'-'A'+1) + ('z'-'a'+1) + (byte-'0')); 824 else if (byte == '+') 825 decodedBits = ('Z'-'A'+1) + ('z'-'a'+1) + ('9'-'0'+1); 826 else if (byte == '/') 827 decodedBits = ('Z'-'A'+1) + ('z'-'a'+1) + ('9'-'0'+2); 828 else if (byte == '=') 829 { 830 // Padding at end - remove last byte. 831 if (image->data.empty()) 832 throw TestResultParseError("Malformed base64 data"); 833 image->data.pop_back(); 834 continue; 835 } 836 else 837 continue; // Not an B64 input character. 838 839 int phase = m_base64DecodeOffset % 4; 840 841 if (phase == 0) 842 image->data.resize(image->data.size()+3, 0); 843 844 if ((int)image->data.size() < (m_base64DecodeOffset>>2)*3 + 3) 845 throw TestResultParseError("Malformed base64 data"); 846 deUint8* outPtr = &image->data[(m_base64DecodeOffset>>2)*3]; 847 848 switch (phase) 849 { 850 case 0: outPtr[0] |= (deUint8)(decodedBits<<2); break; 851 case 1: outPtr[0] |= (deUint8)(decodedBits>>4); outPtr[1] |= (deUint8)((decodedBits&0xF)<<4); break; 852 case 2: outPtr[1] |= (deUint8)(decodedBits>>2); outPtr[2] |= (deUint8)((decodedBits&0x3)<<6); break; 853 case 3: outPtr[2] |= decodedBits; break; 854 default: 855 DE_ASSERT(false); 856 } 857 858 m_base64DecodeOffset += 1; 859 } 860 861 break; 862 } 863 864 default: 865 // Just ignore data. 866 break; 867 } 868} 869 870//! Helper for parsing TestCaseResult from TestCaseResultData. 871void parseTestCaseResultFromData (TestResultParser* parser, TestCaseResult* result, const TestCaseResultData& data) 872{ 873 DE_ASSERT(result->resultItems.getNumItems() == 0); 874 875 // Initialize status codes etc. from data. 876 result->casePath = data.getTestCasePath(); 877 result->caseType = TESTCASETYPE_SELF_VALIDATE; 878 result->statusCode = data.getStatusCode(); 879 result->statusDetails = data.getStatusDetails(); 880 881 if (data.getDataSize() > 0) 882 { 883 parser->init(result); 884 885 const TestResultParser::ParseResult parseResult = parser->parse(data.getData(), data.getDataSize()); 886 887 if (result->statusCode == TESTSTATUSCODE_LAST) 888 { 889 result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR; 890 891 if (parseResult == TestResultParser::PARSERESULT_ERROR) 892 result->statusDetails = "Test case result parsing failed"; 893 else if (parseResult != TestResultParser::PARSERESULT_COMPLETE) 894 result->statusDetails = "Incomplete test case result"; 895 else 896 result->statusDetails = "Test case result is missing <Result> item"; 897 } 898 } 899 else if (result->statusCode == TESTSTATUSCODE_LAST) 900 { 901 result->statusCode = TESTSTATUSCODE_TERMINATED; 902 result->statusDetails = "Empty test case result"; 903 } 904 905 if (result->casePath.empty()) 906 throw Error("Empty test case path in result"); 907 908 if (result->caseType == TESTCASETYPE_LAST) 909 throw Error("Invalid test case type in result"); 910 911 DE_ASSERT(result->statusCode != TESTSTATUSCODE_LAST); 912} 913 914} // xe 915