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