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