blob: eba5ffd8b27dc38eb17d9691eb64c03d5b8fcf5d [file] [log] [blame]
Jarkko Poyry3c827362014-09-02 11:48:52 +03001/*-------------------------------------------------------------------------
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
33using std::string;
34using std::vector;
35
36namespace xe
37{
38
39static inline int toInt (const char* str)
40{
41 return atoi(str);
42}
43
44static inline double toDouble (const char* str)
45{
46 return atof(str);
47}
48
49static 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
59static inline bool toBool (const char* str)
60{
61 return deStringEqual(str, "OK") || deStringEqual(str, "True");
62}
63
64static 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
78struct EnumMapEntry
79{
80 deUint32 hash;
81 const char* name;
82 int value;
83};
84
85static 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
103static 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 },
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500118 { 0xaee4380a, "SpirVAssemblySource", ri::TYPE_SPIRVSOURCE },
Jarkko Poyry3c827362014-09-02 11:48:52 +0300119 { 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
132static const EnumMapEntry s_imageFormatMap[] =
133{
134 { 0xcc4ffac8, "RGB888", ri::Image::FORMAT_RGB888 },
135 { 0x20dcb0c1, "RGBA8888", ri::Image::FORMAT_RGBA8888 }
136};
137
138static const EnumMapEntry s_compressionMap[] =
139{
140 { 0x7c89bbd5, "None", ri::Image::COMPRESSION_NONE },
141 { 0x0b88118a, "PNG", ri::Image::COMPRESSION_PNG }
142};
143
144static 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
154static 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
162static 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};
170
171static const EnumMapEntry s_sampleValueTagMap[] =
172{
173 { 0xddf2d0d1, "Predictor", ri::ValueInfo::VALUETAG_PREDICTOR },
174 { 0x9bee2c34, "Response", ri::ValueInfo::VALUETAG_RESPONSE },
175};
176
177#if defined(DE_DEBUG)
178static void printHashes (const char* name, const EnumMapEntry* entries, int numEntries)
179{
180 printf("%s:\n", name);
181
182 for (int ndx = 0; ndx < numEntries; ndx++)
183 printf("0x%08x\t%s\n", deStringHash(entries[ndx].name), entries[ndx].name);
184
185 printf("\n");
186}
187
188#define PRINT_HASHES(MAP) printHashes(#MAP, MAP, DE_LENGTH_OF_ARRAY(MAP))
189
190void TestResultParser_printHashes (void)
191{
192 PRINT_HASHES(s_statusCodeMap);
193 PRINT_HASHES(s_resultItemMap);
194 PRINT_HASHES(s_imageFormatMap);
195 PRINT_HASHES(s_compressionMap);
196 PRINT_HASHES(s_shaderTypeFromTagMap);
197 PRINT_HASHES(s_testTypeMap);
198 PRINT_HASHES(s_logVersionMap);
199 PRINT_HASHES(s_sampleValueTagMap);
200}
201#endif
202
203static inline int getEnumValue (const char* enumName, const EnumMapEntry* entries, int numEntries, const char* name)
204{
205 deUint32 hash = deStringHash(name);
206
207 for (int ndx = 0; ndx < numEntries; ndx++)
208 {
209 if (entries[ndx].hash == hash && deStringEqual(entries[ndx].name, name))
210 return entries[ndx].value;
211 }
212
213 throw TestResultParseError(string("Could not map '") + name + "' to " + enumName);
214}
215
216TestStatusCode getTestStatusCode (const char* statusCode)
217{
218 return (TestStatusCode)getEnumValue("status code", s_statusCodeMap, DE_LENGTH_OF_ARRAY(s_statusCodeMap), statusCode);
219}
220
221static ri::Type getResultItemType (const char* elemName)
222{
223 return (ri::Type)getEnumValue("result item type", s_resultItemMap, DE_LENGTH_OF_ARRAY(s_resultItemMap), elemName);
224}
225
226static ri::Image::Format getImageFormat (const char* imageFormat)
227{
228 return (ri::Image::Format)getEnumValue("image format", s_imageFormatMap, DE_LENGTH_OF_ARRAY(s_imageFormatMap), imageFormat);
229}
230
231static ri::Image::Compression getImageCompression (const char* compression)
232{
233 return (ri::Image::Compression)getEnumValue("image compression", s_compressionMap, DE_LENGTH_OF_ARRAY(s_compressionMap), compression);
234}
235
236static ri::Shader::ShaderType getShaderTypeFromTagName (const char* shaderType)
237{
238 return (ri::Shader::ShaderType)getEnumValue("shader type", s_shaderTypeFromTagMap, DE_LENGTH_OF_ARRAY(s_shaderTypeFromTagMap), shaderType);
239}
240
241static TestCaseType getTestCaseType (const char* caseType)
242{
243 return (TestCaseType)getEnumValue("test case type", s_testTypeMap, DE_LENGTH_OF_ARRAY(s_testTypeMap), caseType);
244}
245
246static TestLogVersion getTestLogVersion (const char* logVersion)
247{
248 return (TestLogVersion)getEnumValue("test log version", s_logVersionMap, DE_LENGTH_OF_ARRAY(s_logVersionMap), logVersion);
249}
250
251static ri::ValueInfo::ValueTag getSampleValueTag (const char* tag)
252{
253 return (ri::ValueInfo::ValueTag)getEnumValue("sample value tag", s_sampleValueTagMap, DE_LENGTH_OF_ARRAY(s_sampleValueTagMap), tag);
254}
255
256static 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
272static 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 deInt64 num = toInt64(stripLeadingWhitespace(value.c_str()));
284 return ri::NumericValue(num);
285 }
286}
287
288TestResultParser::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
297TestResultParser::~TestResultParser (void)
298{
299}
300
301void 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
314void TestResultParser::init (TestCaseResult* dstResult)
315{
316 clear();
317 m_result = dstResult;
318 m_state = STATE_INITIALIZED;
319 m_curItemList = &dstResult->resultItems;
320}
321
322TestResultParser::ParseResult TestResultParser::parse (const deUint8* 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 ||
337 curElement == xml::ELEMENT_END_OF_STRING)
338 break;
339
340 switch (curElement)
341 {
342 case xml::ELEMENT_START: handleElementStart(); break;
343 case xml::ELEMENT_END: handleElementEnd(); break;
344 case xml::ELEMENT_DATA: handleData(); break;
345
346 default:
347 DE_ASSERT(false);
348 }
349
350 resultChanged = true;
351 m_xmlParser.advance();
352 }
353
354 if (m_xmlParser.getElement() == xml::ELEMENT_END_OF_STRING)
355 {
356 if (m_state != STATE_TEST_CASE_RESULT_ENDED)
357 throw TestResultParseError("Unexpected end of log data");
358
359 return PARSERESULT_COMPLETE;
360 }
361 else
362 return resultChanged ? PARSERESULT_CHANGED
363 : PARSERESULT_NOT_CHANGED;
364 }
365 catch (const TestResultParseError& e)
366 {
367 // Set error code to result.
368 m_result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR;
369 m_result->statusDetails = e.what();
370
371 return PARSERESULT_ERROR;
372 }
373 catch (const xml::ParseError& e)
374 {
375 // Set error code to result.
376 m_result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR;
377 m_result->statusDetails = e.what();
378
379 return PARSERESULT_ERROR;
380 }
381}
382
383const char* TestResultParser::getAttribute (const char* name)
384{
385 if (!m_xmlParser.hasAttribute(name))
386 throw TestResultParseError(string("Missing attribute '") + name + "' in <" + m_xmlParser.getElementName() + ">");
387
388 return m_xmlParser.getAttribute(name);
389}
390
391ri::Item* TestResultParser::getCurrentItem (void)
392{
393 return !m_itemStack.empty() ? m_itemStack.back() : DE_NULL;
394}
395
396ri::List* TestResultParser::getCurrentItemList (void)
397{
398 DE_ASSERT(m_curItemList);
399 return m_curItemList;
400}
401
402void TestResultParser::updateCurrentItemList (void)
403{
404 m_curItemList = DE_NULL;
405
406 for (vector<ri::Item*>::reverse_iterator i = m_itemStack.rbegin(); i != m_itemStack.rend(); i++)
407 {
408 ri::Item* item = *i;
409 ri::Type type = item->getType();
410
411 if (type == ri::TYPE_IMAGESET)
412 m_curItemList = &static_cast<ri::ImageSet*>(item)->images;
413 else if (type == ri::TYPE_SECTION)
414 m_curItemList = &static_cast<ri::Section*>(item)->items;
415 else if (type == ri::TYPE_EGLCONFIGSET)
416 m_curItemList = &static_cast<ri::EglConfigSet*>(item)->configs;
417 else if (type == ri::TYPE_SHADERPROGRAM)
418 m_curItemList = &static_cast<ri::ShaderProgram*>(item)->shaders;
419
420 if (m_curItemList)
421 break;
422 }
423
424 if (!m_curItemList)
425 m_curItemList = &m_result->resultItems;
426}
427
428void TestResultParser::pushItem (ri::Item* item)
429{
430 m_itemStack.push_back(item);
431 updateCurrentItemList();
432}
433
434void TestResultParser::popItem (void)
435{
436 m_itemStack.pop_back();
437 updateCurrentItemList();
438}
439
440void TestResultParser::handleElementStart (void)
441{
442 const char* elemName = m_xmlParser.getElementName();
443
444 if (m_state == STATE_INITIALIZED)
445 {
446 // Expect TestCaseResult.
447 if (!deStringEqual(elemName, "TestCaseResult"))
448 throw TestResultParseError(string("Expected <TestCaseResult>, got <") + elemName + ">");
449
450 const char* version = getAttribute("Version");
451 m_logVersion = getTestLogVersion(version);
452 // \note Currently assumed that all known log versions are supported.
453
454 m_result->casePath = getAttribute("CasePath");
455 m_result->caseType = TESTCASETYPE_SELF_VALIDATE;
456
457 if (m_xmlParser.hasAttribute("CaseType"))
458 m_result->caseType = getTestCaseType(m_xmlParser.getAttribute("CaseType"));
459 else
460 {
461 // Do guess based on path for legacy log files.
462 if (m_logVersion >= TESTLOGVERSION_0_3_2)
463 throw TestResultParseError("Missing CaseType attribute in <TestCaseResult>");
464 m_result->caseType = getTestCaseTypeFromPath(m_result->casePath.c_str());
465 }
466
467 m_state = STATE_IN_TEST_CASE_RESULT;
468 }
469 else
470 {
471 ri::List* curList = getCurrentItemList();
472 ri::Type itemType = getResultItemType(elemName);
473 ri::Item* item = DE_NULL;
474 ri::Item* parentItem = getCurrentItem();
475 ri::Type parentType = parentItem ? parentItem->getType() : ri::TYPE_LAST;
476
477 switch (itemType)
478 {
479 case ri::TYPE_RESULT:
480 {
481 ri::Result* result = curList->allocItem<ri::Result>();
482 result->statusCode = getTestStatusCode(getAttribute("StatusCode"));
483 item = result;
484 break;
485 }
486
487 case ri::TYPE_TEXT:
488 item = curList->allocItem<ri::Text>();
489 break;
490
491 case ri::TYPE_SECTION:
492 {
493 ri::Section* section = curList->allocItem<ri::Section>();
494 section->name = getAttribute("Name");
495 section->description = getAttribute("Description");
496 item = section;
497 break;
498 }
499
500 case ri::TYPE_NUMBER:
501 {
502 ri::Number* number = curList->allocItem<ri::Number>();
503 number->name = getAttribute("Name");
504 number->description = getAttribute("Description");
505 number->unit = getAttribute("Unit");
506
507 if (m_xmlParser.hasAttribute("Tag"))
508 number->tag = m_xmlParser.getAttribute("Tag");
509
510 item = number;
511
512 m_curNumValue.clear();
513 break;
514 }
515
516 case ri::TYPE_IMAGESET:
517 {
518 ri::ImageSet* imageSet = curList->allocItem<ri::ImageSet>();
519 imageSet->name = getAttribute("Name");
520 imageSet->description = getAttribute("Description");
521 item = imageSet;
522 break;
523 }
524
525 case ri::TYPE_IMAGE:
526 {
527 ri::Image* image = curList->allocItem<ri::Image>();
528 image->name = getAttribute("Name");
529 image->description = getAttribute("Description");
530 image->width = toInt(getAttribute("Width"));
531 image->height = toInt(getAttribute("Height"));
532 image->format = getImageFormat(getAttribute("Format"));
533 image->compression = getImageCompression(getAttribute("CompressionMode"));
534 item = image;
535 break;
536 }
537
538 case ri::TYPE_SHADERPROGRAM:
539 {
540 ri::ShaderProgram* shaderProgram = curList->allocItem<ri::ShaderProgram>();
541 shaderProgram->linkStatus = toBool(getAttribute("LinkStatus"));
542 item = shaderProgram;
543 break;
544 }
545
546 case ri::TYPE_SHADER:
547 {
548 if (parentType != ri::TYPE_SHADERPROGRAM)
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500549 throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>");
Jarkko Poyry3c827362014-09-02 11:48:52 +0300550
551 ri::Shader* shader = curList->allocItem<ri::Shader>();
552
553 shader->shaderType = getShaderTypeFromTagName(elemName);
554 shader->compileStatus = toBool(getAttribute("CompileStatus"));
555
556 item = shader;
557 break;
558 }
559
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500560 case ri::TYPE_SPIRVSOURCE:
561 {
562 if (parentType != ri::TYPE_SHADERPROGRAM)
563 throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>");
564 item = curList->allocItem<ri::SpirVSource>();
565 break;
566 }
567
Jarkko Poyry3c827362014-09-02 11:48:52 +0300568 case ri::TYPE_SHADERSOURCE:
569 if (parentType == ri::TYPE_SHADER)
570 item = &static_cast<ri::Shader*>(parentItem)->source;
571 else
572 throw TestResultParseError("Unexpected <ShaderSource>");
573 break;
574
575 case ri::TYPE_INFOLOG:
576 if (parentType == ri::TYPE_SHADERPROGRAM)
577 item = &static_cast<ri::ShaderProgram*>(parentItem)->linkInfoLog;
578 else if (parentType == ri::TYPE_SHADER)
579 item = &static_cast<ri::Shader*>(parentItem)->infoLog;
580 else if (parentType == ri::TYPE_COMPILEINFO)
581 item = &static_cast<ri::CompileInfo*>(parentItem)->infoLog;
582 else
583 throw TestResultParseError("Unexpected <InfoLog>");
584 break;
585
586 case ri::TYPE_KERNELSOURCE:
587 item = curList->allocItem<ri::KernelSource>();
588 break;
589
590 case ri::TYPE_COMPILEINFO:
591 {
592 ri::CompileInfo* info = curList->allocItem<ri::CompileInfo>();
593 info->name = getAttribute("Name");
594 info->description = getAttribute("Description");
595 info->compileStatus = toBool(getAttribute("CompileStatus"));
596 item = info;
597 break;
598 }
599
600 case ri::TYPE_EGLCONFIGSET:
601 {
602 ri::EglConfigSet* set = curList->allocItem<ri::EglConfigSet>();
603 set->name = getAttribute("Name");
604 set->description = m_xmlParser.hasAttribute("Description") ? m_xmlParser.getAttribute("Description") : "";
605 item = set;
606 break;
607 }
608
609 case ri::TYPE_EGLCONFIG:
610 {
611 ri::EglConfig* config = curList->allocItem<ri::EglConfig>();
612 config->bufferSize = toInt(getAttribute("BufferSize"));
613 config->redSize = toInt(getAttribute("RedSize"));
614 config->greenSize = toInt(getAttribute("GreenSize"));
615 config->blueSize = toInt(getAttribute("BlueSize"));
616 config->luminanceSize = toInt(getAttribute("LuminanceSize"));
617 config->alphaSize = toInt(getAttribute("AlphaSize"));
618 config->alphaMaskSize = toInt(getAttribute("AlphaMaskSize"));
619 config->bindToTextureRGB = toBool(getAttribute("BindToTextureRGB"));
620 config->bindToTextureRGBA = toBool(getAttribute("BindToTextureRGBA"));
621 config->colorBufferType = getAttribute("ColorBufferType");
622 config->configCaveat = getAttribute("ConfigCaveat");
623 config->configID = toInt(getAttribute("ConfigID"));
624 config->conformant = getAttribute("Conformant");
625 config->depthSize = toInt(getAttribute("DepthSize"));
626 config->level = toInt(getAttribute("Level"));
627 config->maxPBufferWidth = toInt(getAttribute("MaxPBufferWidth"));
628 config->maxPBufferHeight = toInt(getAttribute("MaxPBufferHeight"));
629 config->maxPBufferPixels = toInt(getAttribute("MaxPBufferPixels"));
630 config->maxSwapInterval = toInt(getAttribute("MaxSwapInterval"));
631 config->minSwapInterval = toInt(getAttribute("MinSwapInterval"));
632 config->nativeRenderable = toBool(getAttribute("NativeRenderable"));
633 config->renderableType = getAttribute("RenderableType");
634 config->sampleBuffers = toInt(getAttribute("SampleBuffers"));
635 config->samples = toInt(getAttribute("Samples"));
636 config->stencilSize = toInt(getAttribute("StencilSize"));
637 config->surfaceTypes = getAttribute("SurfaceTypes");
638 config->transparentType = getAttribute("TransparentType");
639 config->transparentRedValue = toInt(getAttribute("TransparentRedValue"));
640 config->transparentGreenValue = toInt(getAttribute("TransparentGreenValue"));
641 config->transparentBlueValue = toInt(getAttribute("TransparentBlueValue"));
642 item = config;
643 break;
644 }
645
646 case ri::TYPE_SAMPLELIST:
647 {
648 ri::SampleList* list = curList->allocItem<ri::SampleList>();
649 list->name = getAttribute("Name");
650 list->description = getAttribute("Description");
651 item = list;
652 break;
653 }
654
655 case ri::TYPE_SAMPLEINFO:
656 {
657 if (parentType != ri::TYPE_SAMPLELIST)
658 throw TestResultParseError("<SampleInfo> outside of <SampleList>");
659
660 ri::SampleList* list = static_cast<ri::SampleList*>(parentItem);
661 ri::SampleInfo* info = &list->sampleInfo;
662
663 item = info;
664 break;
665 }
666
667 case ri::TYPE_VALUEINFO:
668 {
669 if (parentType != ri::TYPE_SAMPLEINFO)
670 throw TestResultParseError("<ValueInfo> outside of <SampleInfo>");
671
672 ri::SampleInfo* sampleInfo = static_cast<ri::SampleInfo*>(parentItem);
673 ri::ValueInfo* valueInfo = sampleInfo->valueInfos.allocItem<ri::ValueInfo>();
674
675 valueInfo->name = getAttribute("Name");
676 valueInfo->description = getAttribute("Description");
677 valueInfo->tag = getSampleValueTag(getAttribute("Tag"));
678
679 if (m_xmlParser.hasAttribute("Unit"))
680 valueInfo->unit = getAttribute("Unit");
681
682 item = valueInfo;
683 break;
684 }
685
686 case ri::TYPE_SAMPLE:
687 {
688 if (parentType != ri::TYPE_SAMPLELIST)
689 throw TestResultParseError("<Sample> outside of <SampleList>");
690
691 ri::SampleList* list = static_cast<ri::SampleList*>(parentItem);
692 ri::Sample* sample = list->samples.allocItem<ri::Sample>();
693
694 item = sample;
695 break;
696 }
697
698 case ri::TYPE_SAMPLEVALUE:
699 {
700 if (parentType != ri::TYPE_SAMPLE)
701 throw TestResultParseError("<Value> outside of <Sample>");
702
703 ri::Sample* sample = static_cast<ri::Sample*>(parentItem);
704 ri::SampleValue* value = sample->values.allocItem<ri::SampleValue>();
705
706 item = value;
707 break;
708 }
709
710 default:
711 throw TestResultParseError(string("Unsupported element '") + elemName + ("'"));
712 }
713
714 DE_ASSERT(item);
715 pushItem(item);
716
717 // Reset base64 decoding offset.
718 m_base64DecodeOffset = 0;
719 }
720}
721
722void TestResultParser::handleElementEnd (void)
723{
724 const char* elemName = m_xmlParser.getElementName();
725
726 if (m_state != STATE_IN_TEST_CASE_RESULT)
727 throw TestResultParseError(string("Unexpected </") + elemName + "> outside of <TestCaseResult>");
728
729 if (deStringEqual(elemName, "TestCaseResult"))
730 {
731 // Logs from buggy test cases may contain invalid XML.
732 // DE_ASSERT(getCurrentItem() == DE_NULL);
733 // \todo [2012-11-22 pyry] Log warning.
734
735 m_state = STATE_TEST_CASE_RESULT_ENDED;
736 }
737 else
738 {
739 ri::Type itemType = getResultItemType(elemName);
740 ri::Item* curItem = getCurrentItem();
741
742 if (!curItem || itemType != curItem->getType())
743 throw TestResultParseError(string("Unexpected </") + elemName + ">");
744
745 if (itemType == ri::TYPE_RESULT)
746 {
747 ri::Result* result = static_cast<ri::Result*>(curItem);
748 m_result->statusCode = result->statusCode;
749 m_result->statusDetails = result->details;
750 }
751 else if (itemType == ri::TYPE_NUMBER)
752 {
753 // Parse value for number.
754 ri::Number* number = static_cast<ri::Number*>(curItem);
755 number->value = getNumericValue(m_curNumValue);
756 m_curNumValue.clear();
757 }
758 else if (itemType == ri::TYPE_SAMPLEVALUE)
759 {
760 ri::SampleValue* value = static_cast<ri::SampleValue*>(curItem);
761 value->value = getNumericValue(m_curNumValue);
762 m_curNumValue.clear();
763 }
764
765 popItem();
766 }
767}
768
769void TestResultParser::handleData (void)
770{
771 ri::Item* curItem = getCurrentItem();
772 ri::Type type = curItem ? curItem->getType() : ri::TYPE_LAST;
773
774 switch (type)
775 {
776 case ri::TYPE_RESULT:
777 m_xmlParser.appendDataStr(static_cast<ri::Result*>(curItem)->details);
778 break;
779
780 case ri::TYPE_TEXT:
781 m_xmlParser.appendDataStr(static_cast<ri::Text*>(curItem)->text);
782 break;
783
784 case ri::TYPE_SHADERSOURCE:
785 m_xmlParser.appendDataStr(static_cast<ri::ShaderSource*>(curItem)->source);
786 break;
787
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500788 case ri::TYPE_SPIRVSOURCE:
789 m_xmlParser.appendDataStr(static_cast<ri::SpirVSource*>(curItem)->source);
790 break;
791
Jarkko Poyry3c827362014-09-02 11:48:52 +0300792 case ri::TYPE_INFOLOG:
793 m_xmlParser.appendDataStr(static_cast<ri::InfoLog*>(curItem)->log);
794 break;
795
796 case ri::TYPE_KERNELSOURCE:
797 m_xmlParser.appendDataStr(static_cast<ri::KernelSource*>(curItem)->source);
798 break;
799
800 case ri::TYPE_NUMBER:
801 case ri::TYPE_SAMPLEVALUE:
802 m_xmlParser.appendDataStr(m_curNumValue);
803 break;
804
805 case ri::TYPE_IMAGE:
806 {
807 ri::Image* image = static_cast<ri::Image*>(curItem);
808
809 // Base64 decode.
810 int numBytesIn = m_xmlParser.getDataSize();
811
812 for (int inNdx = 0; inNdx < numBytesIn; inNdx++)
813 {
814 deUint8 byte = m_xmlParser.getDataByte(inNdx);
815 deUint8 decodedBits = 0;
816
817 if (de::inRange<deInt8>(byte, 'A', 'Z'))
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700818 decodedBits = (deUint8)(byte - 'A');
Jarkko Poyry3c827362014-09-02 11:48:52 +0300819 else if (de::inRange<deInt8>(byte, 'a', 'z'))
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700820 decodedBits = (deUint8)(('Z'-'A'+1) + (byte-'a'));
Jarkko Poyry3c827362014-09-02 11:48:52 +0300821 else if (de::inRange<deInt8>(byte, '0', '9'))
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700822 decodedBits = (deUint8)(('Z'-'A'+1) + ('z'-'a'+1) + (byte-'0'));
Jarkko Poyry3c827362014-09-02 11:48:52 +0300823 else if (byte == '+')
824 decodedBits = ('Z'-'A'+1) + ('z'-'a'+1) + ('9'-'0'+1);
825 else if (byte == '/')
826 decodedBits = ('Z'-'A'+1) + ('z'-'a'+1) + ('9'-'0'+2);
827 else if (byte == '=')
828 {
829 // Padding at end - remove last byte.
830 if (image->data.empty())
831 throw TestResultParseError("Malformed base64 data");
832 image->data.pop_back();
833 continue;
834 }
835 else
836 continue; // Not an B64 input character.
837
838 int phase = m_base64DecodeOffset % 4;
839
840 if (phase == 0)
841 image->data.resize(image->data.size()+3, 0);
842
843 if ((int)image->data.size() < (m_base64DecodeOffset>>2)*3 + 3)
844 throw TestResultParseError("Malformed base64 data");
845 deUint8* outPtr = &image->data[(m_base64DecodeOffset>>2)*3];
846
847 switch (phase)
848 {
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700849 case 0: outPtr[0] |= (deUint8)(decodedBits<<2); break;
850 case 1: outPtr[0] |= (deUint8)(decodedBits>>4); outPtr[1] |= (deUint8)((decodedBits&0xF)<<4); break;
851 case 2: outPtr[1] |= (deUint8)(decodedBits>>2); outPtr[2] |= (deUint8)((decodedBits&0x3)<<6); break;
852 case 3: outPtr[2] |= decodedBits; break;
Jarkko Poyry3c827362014-09-02 11:48:52 +0300853 default:
854 DE_ASSERT(false);
855 }
856
857 m_base64DecodeOffset += 1;
858 }
859
860 break;
861 }
862
863 default:
864 // Just ignore data.
865 break;
866 }
867}
868
869//! Helper for parsing TestCaseResult from TestCaseResultData.
870void parseTestCaseResultFromData (TestResultParser* parser, TestCaseResult* result, const TestCaseResultData& data)
871{
872 DE_ASSERT(result->resultItems.getNumItems() == 0);
873
874 // Initialize status codes etc. from data.
875 result->casePath = data.getTestCasePath();
876 result->caseType = TESTCASETYPE_SELF_VALIDATE;
877 result->statusCode = data.getStatusCode();
878 result->statusDetails = data.getStatusDetails();
879
880 if (data.getDataSize() > 0)
881 {
882 parser->init(result);
883
884 const TestResultParser::ParseResult parseResult = parser->parse(data.getData(), data.getDataSize());
885
886 if (result->statusCode == TESTSTATUSCODE_LAST)
887 {
888 result->statusCode = TESTSTATUSCODE_INTERNAL_ERROR;
889
890 if (parseResult == TestResultParser::PARSERESULT_ERROR)
891 result->statusDetails = "Test case result parsing failed";
892 else if (parseResult != TestResultParser::PARSERESULT_COMPLETE)
893 result->statusDetails = "Incomplete test case result";
894 else
895 result->statusDetails = "Test case result is missing <Result> item";
896 }
897 }
898 else if (result->statusCode == TESTSTATUSCODE_LAST)
899 {
900 result->statusCode = TESTSTATUSCODE_TERMINATED;
901 result->statusDetails = "Empty test case result";
902 }
903
904 if (result->casePath.empty())
905 throw Error("Empty test case path in result");
906
907 if (result->caseType == TESTCASETYPE_LAST)
908 throw Error("Invalid test case type in result");
909
910 DE_ASSERT(result->statusCode != TESTSTATUSCODE_LAST);
911}
912
913} // xe