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