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