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