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
33using std::string;
34using std::vector;
35
36namespace xe
37{
38
39static inline int toInt (const char* str)
40{
41	return atoi(str);
42}
43
44static inline double toDouble (const char* str)
45{
46	return atof(str);
47}
48
49static inline deInt64 toInt64 (const char* str)
50{
51	std::istringstream	s	(str);
52	deInt64				val;
53
54	s >> val;
55
56	return val;
57}
58
59static inline bool toBool (const char* str)
60{
61	return deStringEqual(str, "OK") || deStringEqual(str, "True");
62}
63
64static const char* stripLeadingWhitespace (const char* str)
65{
66	int whitespaceCount = 0;
67
68	while (str[whitespaceCount]	!= 0	&&
69		   (str[whitespaceCount] == ' '		||
70			str[whitespaceCount] == '\t'	||
71			str[whitespaceCount] == '\r'	||
72			str[whitespaceCount] == '\n'))
73		whitespaceCount += 1;
74
75	return str + whitespaceCount;
76}
77
78struct EnumMapEntry
79{
80	deUint32		hash;
81	const char*		name;
82	int				value;
83};
84
85static const EnumMapEntry s_statusCodeMap[] =
86{
87	{ 0x7c8a99bc,	"Pass",					TESTSTATUSCODE_PASS						},
88	{ 0x7c851ca1,	"Fail",					TESTSTATUSCODE_FAIL						},
89	{ 0x10ecd324,	"QualityWarning",		TESTSTATUSCODE_QUALITY_WARNING			},
90	{ 0x341ae835,	"CompatibilityWarning",	TESTSTATUSCODE_COMPATIBILITY_WARNING	},
91	{ 0x058acbca,	"Pending",				TESTSTATUSCODE_PENDING					},
92	{ 0xc4d74b26,	"Running",				TESTSTATUSCODE_RUNNING					},
93	{ 0x6409f93c,	"NotSupported",			TESTSTATUSCODE_NOT_SUPPORTED			},
94	{ 0xfa5a9ab7,	"ResourceError",		TESTSTATUSCODE_RESOURCE_ERROR			},
95	{ 0xad6793ec,	"InternalError",		TESTSTATUSCODE_INTERNAL_ERROR			},
96	{ 0x838f3034,	"Canceled",				TESTSTATUSCODE_CANCELED					},
97	{ 0x42b6efac,	"Timeout",				TESTSTATUSCODE_TIMEOUT					},
98	{ 0x0cfb98f6,	"Crash",				TESTSTATUSCODE_CRASH					},
99	{ 0xe326e01d,	"Disabled",				TESTSTATUSCODE_DISABLED					},
100	{ 0x77061af2,	"Terminated",			TESTSTATUSCODE_TERMINATED				}
101};
102
103static const EnumMapEntry s_resultItemMap[] =
104{
105	{ 0xce8ac2e4,	"Result",				ri::TYPE_RESULT			},
106	{ 0x7c8cdcea,	"Text",					ri::TYPE_TEXT			},
107	{ 0xc6540c6e,	"Number",				ri::TYPE_NUMBER			},
108	{ 0x0d656c88,	"Image",				ri::TYPE_IMAGE			},
109	{ 0x8ac9ee14,	"ImageSet",				ri::TYPE_IMAGESET		},
110	{ 0x1181fa5a,	"VertexShader",			ri::TYPE_SHADER			},
111	{ 0xa93daef0,	"FragmentShader",		ri::TYPE_SHADER			},
112	{ 0x8f066128,	"GeometryShader",		ri::TYPE_SHADER			},
113	{ 0x235a931c,	"TessControlShader",	ri::TYPE_SHADER			},
114	{ 0xa1bf7153,	"TessEvaluationShader",	ri::TYPE_SHADER			},
115	{ 0x6c1415d9,	"ComputeShader",		ri::TYPE_SHADER			},
116	{ 0x72863a54,	"ShaderProgram",		ri::TYPE_SHADERPROGRAM	},
117	{ 0xb4efc08d,	"ShaderSource",			ri::TYPE_SHADERSOURCE	},
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
132static const EnumMapEntry s_imageFormatMap[] =
133{
134	{ 0xcc4ffac8,	"RGB888",		ri::Image::FORMAT_RGB888	},
135	{ 0x20dcb0c1,	"RGBA8888",		ri::Image::FORMAT_RGBA8888	}
136};
137
138static const EnumMapEntry s_compressionMap[] =
139{
140	{ 0x7c89bbd5,	"None",			ri::Image::COMPRESSION_NONE	},
141	{ 0x0b88118a,	"PNG",			ri::Image::COMPRESSION_PNG	}
142};
143
144static const EnumMapEntry s_shaderTypeFromTagMap[] =
145{
146	{ 0x1181fa5a,	"VertexShader",			ri::Shader::SHADERTYPE_VERTEX			},
147	{ 0xa93daef0,	"FragmentShader",		ri::Shader::SHADERTYPE_FRAGMENT			},
148	{ 0x8f066128,	"GeometryShader",		ri::Shader::SHADERTYPE_GEOMETRY			},
149	{ 0x235a931c,	"TessControlShader",	ri::Shader::SHADERTYPE_TESS_CONTROL		},
150	{ 0xa1bf7153,	"TessEvaluationShader",	ri::Shader::SHADERTYPE_TESS_EVALUATION	},
151	{ 0x6c1415d9,	"ComputeShader",		ri::Shader::SHADERTYPE_COMPUTE			},
152};
153
154static const EnumMapEntry s_testTypeMap[] =
155{
156	{ 0x7fa80959,	"SelfValidate",	TESTCASETYPE_SELF_VALIDATE	},
157	{ 0xdb797567,	"Capability",	TESTCASETYPE_CAPABILITY		},
158	{ 0x2ca3ec10,	"Accuracy",		TESTCASETYPE_ACCURACY		},
159	{ 0xa48ac277,	"Performance",	TESTCASETYPE_PERFORMANCE	}
160};
161
162static const EnumMapEntry s_logVersionMap[] =
163{
164	{ 0x0b7dac93,	"0.2.0",		TESTLOGVERSION_0_2_0	},
165	{ 0x0b7db0d4,	"0.3.0",		TESTLOGVERSION_0_3_0	},
166	{ 0x0b7db0d5,	"0.3.1",		TESTLOGVERSION_0_3_1	},
167	{ 0x0b7db0d6,	"0.3.2",		TESTLOGVERSION_0_3_2	},
168	{ 0x0b7db0d7,	"0.3.3",		TESTLOGVERSION_0_3_3	},
169	{ 0x0b7db0d8,	"0.3.4",		TESTLOGVERSION_0_3_4	}
170};
171
172static 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)
179static 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
191void 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
204static 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
217TestStatusCode getTestStatusCode (const char* statusCode)
218{
219	return (TestStatusCode)getEnumValue("status code", s_statusCodeMap, DE_LENGTH_OF_ARRAY(s_statusCodeMap), statusCode);
220}
221
222static 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
227static 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
232static 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
237static 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
242static TestCaseType getTestCaseType (const char* caseType)
243{
244	return (TestCaseType)getEnumValue("test case type", s_testTypeMap, DE_LENGTH_OF_ARRAY(s_testTypeMap), caseType);
245}
246
247static TestLogVersion getTestLogVersion (const char* logVersion)
248{
249	return (TestLogVersion)getEnumValue("test log version", s_logVersionMap, DE_LENGTH_OF_ARRAY(s_logVersionMap), logVersion);
250}
251
252static 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
257static 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
273static 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
289TestResultParser::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
298TestResultParser::~TestResultParser (void)
299{
300}
301
302void 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
315void TestResultParser::init (TestCaseResult* dstResult)
316{
317	clear();
318	m_result		= dstResult;
319	m_state			= STATE_INITIALIZED;
320	m_curItemList	= &dstResult->resultItems;
321}
322
323TestResultParser::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
384const 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
392ri::Item* TestResultParser::getCurrentItem (void)
393{
394	return !m_itemStack.empty() ? m_itemStack.back() : DE_NULL;
395}
396
397ri::List* TestResultParser::getCurrentItemList (void)
398{
399	DE_ASSERT(m_curItemList);
400	return m_curItemList;
401}
402
403void 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
429void TestResultParser::pushItem (ri::Item* item)
430{
431	m_itemStack.push_back(item);
432	updateCurrentItemList();
433}
434
435void TestResultParser::popItem (void)
436{
437	m_itemStack.pop_back();
438	updateCurrentItemList();
439}
440
441void 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
723void 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
770void 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)(decodedBits>>4);	outPtr[1] |= (deUint8)((decodedBits&0xF)<<4);	break;
852					case 2: outPtr[1] |= (deUint8)(decodedBits>>2);	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.
871void 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