xref: /aosp_15_r20/external/deqp/executor/xeTestResultParser.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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