blob: 6d424036729ede022767a220dcb4576d27f6e660 [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 },
Cheryl Weif7139f42021-03-16 15:43:48 +0800100 { 0x77061af2, "Terminated", TESTSTATUSCODE_TERMINATED },
101 { 0xd9e6b393, "Waiver", TESTSTATUSCODE_WAIVER }
Jarkko Poyry3c827362014-09-02 11:48:52 +0300102};
103
104static 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 },
Jason Ekstrandbfb5c192020-07-10 10:23:16 -0500117 { 0x68738b22, "RaygenShader", ri::TYPE_SHADER },
Slawomir Cygan61976fe2020-04-23 12:23:13 +0200118 { 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 },
Jarkko Poyry3c827362014-09-02 11:48:52 +0300123 { 0x72863a54, "ShaderProgram", ri::TYPE_SHADERPROGRAM },
124 { 0xb4efc08d, "ShaderSource", ri::TYPE_SHADERSOURCE },
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500125 { 0xaee4380a, "SpirVAssemblySource", ri::TYPE_SPIRVSOURCE },
Jarkko Poyry3c827362014-09-02 11:48:52 +0300126 { 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 },
Slawomir Cygan61976fe2020-04-23 12:23:13 +0200136 { 0x0e4a4722, "Value", ri::TYPE_SAMPLEVALUE }
Jarkko Poyry3c827362014-09-02 11:48:52 +0300137};
138
139static const EnumMapEntry s_imageFormatMap[] =
140{
141 { 0xcc4ffac8, "RGB888", ri::Image::FORMAT_RGB888 },
142 { 0x20dcb0c1, "RGBA8888", ri::Image::FORMAT_RGBA8888 }
143};
144
145static const EnumMapEntry s_compressionMap[] =
146{
147 { 0x7c89bbd5, "None", ri::Image::COMPRESSION_NONE },
148 { 0x0b88118a, "PNG", ri::Image::COMPRESSION_PNG }
149};
150
151static 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 },
Jason Ekstrandbfb5c192020-07-10 10:23:16 -0500159 { 0x68738b22, "RaygenShader", ri::Shader::SHADERTYPE_RAYGEN },
Slawomir Cygan61976fe2020-04-23 12:23:13 +0200160 { 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 },
Ricardo Garcia241241e2021-10-28 15:36:36 +0200164 { 0x7e50944c, "CallableShader", ri::Shader::SHADERTYPE_CALLABLE },
165 { 0xc3a35d6f, "TaskShader", ri::Shader::SHADERTYPE_TASK },
166 { 0x925c7349, "MeshShader", ri::Shader::SHADERTYPE_MESH },
Jarkko Poyry3c827362014-09-02 11:48:52 +0300167};
168
169static 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
177static 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 },
Mika Isojärvi352d99d2016-02-11 15:35:19 -0800183 { 0x0b7db0d7, "0.3.3", TESTLOGVERSION_0_3_3 },
184 { 0x0b7db0d8, "0.3.4", TESTLOGVERSION_0_3_4 }
Jarkko Poyry3c827362014-09-02 11:48:52 +0300185};
186
187static 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)
194static 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
206void 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
219static 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
232TestStatusCode getTestStatusCode (const char* statusCode)
233{
234 return (TestStatusCode)getEnumValue("status code", s_statusCodeMap, DE_LENGTH_OF_ARRAY(s_statusCodeMap), statusCode);
235}
236
237static 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
242static 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
247static 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
252static 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
257static TestCaseType getTestCaseType (const char* caseType)
258{
259 return (TestCaseType)getEnumValue("test case type", s_testTypeMap, DE_LENGTH_OF_ARRAY(s_testTypeMap), caseType);
260}
261
262static TestLogVersion getTestLogVersion (const char* logVersion)
263{
264 return (TestLogVersion)getEnumValue("test log version", s_logVersionMap, DE_LENGTH_OF_ARRAY(s_logVersionMap), logVersion);
265}
266
267static 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
272static 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
288static 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
304TestResultParser::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
313TestResultParser::~TestResultParser (void)
314{
315}
316
317void 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
330void TestResultParser::init (TestCaseResult* dstResult)
331{
332 clear();
333 m_result = dstResult;
334 m_state = STATE_INITIALIZED;
335 m_curItemList = &dstResult->resultItems;
336}
337
338TestResultParser::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
399const 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
407ri::Item* TestResultParser::getCurrentItem (void)
408{
409 return !m_itemStack.empty() ? m_itemStack.back() : DE_NULL;
410}
411
412ri::List* TestResultParser::getCurrentItemList (void)
413{
414 DE_ASSERT(m_curItemList);
415 return m_curItemList;
416}
417
418void 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
444void TestResultParser::pushItem (ri::Item* item)
445{
446 m_itemStack.push_back(item);
447 updateCurrentItemList();
448}
449
450void TestResultParser::popItem (void)
451{
452 m_itemStack.pop_back();
453 updateCurrentItemList();
454}
455
456void 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
Ricardo Garciadfd5bda2022-04-01 12:01:40 +0200470 m_result->caseVersion = version;
471 m_result->casePath = getAttribute("CasePath");
472 m_result->caseType = TESTCASETYPE_SELF_VALIDATE;
Jarkko Poyry3c827362014-09-02 11:48:52 +0300473
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)
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500566 throw TestResultParseError(string("<") + elemName + "> outside of <ShaderProgram>");
Jarkko Poyry3c827362014-09-02 11:48:52 +0300567
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
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500577 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
Jarkko Poyry3c827362014-09-02 11:48:52 +0300585 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
739void 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
786void 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
Dejan Mircevski133cd2f2016-02-08 10:23:49 -0500805 case ri::TYPE_SPIRVSOURCE:
806 m_xmlParser.appendDataStr(static_cast<ri::SpirVSource*>(curItem)->source);
807 break;
808
Jarkko Poyry3c827362014-09-02 11:48:52 +0300809 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'))
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700835 decodedBits = (deUint8)(byte - 'A');
Jarkko Poyry3c827362014-09-02 11:48:52 +0300836 else if (de::inRange<deInt8>(byte, 'a', 'z'))
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700837 decodedBits = (deUint8)(('Z'-'A'+1) + (byte-'a'));
Jarkko Poyry3c827362014-09-02 11:48:52 +0300838 else if (de::inRange<deInt8>(byte, '0', '9'))
Jarkko Pöyry745d7c62015-05-19 12:24:51 -0700839 decodedBits = (deUint8)(('Z'-'A'+1) + ('z'-'a'+1) + (byte-'0'));
Jarkko Poyry3c827362014-09-02 11:48:52 +0300840 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 {
Alexander Galazin0bd88282017-07-05 17:09:38 +0200866 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;
Jarkko Poyry3c827362014-09-02 11:48:52 +0300870 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.
887void 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