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