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