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