1 /*-------------------------------------------------------------------------
2  * drawElements TestLog Library
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 logging
22  *//*--------------------------------------------------------------------*/
23 
24 #include "qpTestLog.h"
25 #include "qpXmlWriter.h"
26 #include "qpInfo.h"
27 #include "qpDebugOut.h"
28 
29 #include "deMemory.h"
30 #include "deInt32.h"
31 #include "deString.h"
32 
33 #include "deMutex.h"
34 
35 #if defined(QP_SUPPORT_PNG)
36 #	include <png.h>
37 #endif
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42 
43 #if (DE_OS == DE_OS_WIN32)
44 #	include <windows.h>
45 #	include <io.h>
46 #endif
47 
48 #if defined(DE_DEBUG)
49 
50 /* Utils for verifying container (Section, ImageSet, EglConfigSet) usage in debug builds. */
51 
52 typedef enum ContainerType_e
53 {
54 	CONTAINERTYPE_SECTION = 0,
55 	CONTAINERTYPE_IMAGESET,
56 	CONTAINERTYPE_EGLCONFIGSET,
57 	CONTAINERTYPE_SHADERPROGRAM,
58 	CONTAINERTYPE_SAMPLELIST,
59 	CONTAINERTYPE_SAMPLEINFO,
60 	CONTAINERTYPE_SAMPLE,
61 
62 	CONTAINERTYPE_LAST
63 } ContainerType;
64 
childContainersOk(ContainerType type)65 DE_INLINE deBool childContainersOk (ContainerType type)
66 {
67 	return type == CONTAINERTYPE_SECTION || type == CONTAINERTYPE_SAMPLELIST;
68 }
69 
70 enum
71 {
72 	MAX_CONTAINER_STACK_DEPTH		= 32
73 };
74 
75 typedef struct ContainerStack_s
76 {
77 	int				numElements;
78 	ContainerType	elements[MAX_CONTAINER_STACK_DEPTH];
79 } ContainerStack;
80 
ContainerStack_reset(ContainerStack * stack)81 DE_INLINE void ContainerStack_reset (ContainerStack* stack)
82 {
83 	deMemset(stack, 0, sizeof(ContainerStack));
84 }
85 
ContainerStack_isEmpty(const ContainerStack * stack)86 DE_INLINE deBool ContainerStack_isEmpty (const ContainerStack* stack)
87 {
88 	return stack->numElements == 0;
89 }
90 
ContainerStack_push(ContainerStack * stack,ContainerType type)91 DE_INLINE deBool ContainerStack_push (ContainerStack* stack, ContainerType type)
92 {
93 	if (stack->numElements == MAX_CONTAINER_STACK_DEPTH)
94 		return DE_FALSE;
95 
96 	if (stack->numElements > 0 && !childContainersOk(stack->elements[stack->numElements-1]))
97 		return DE_FALSE;
98 
99 	stack->elements[stack->numElements]  = type;
100 	stack->numElements					+= 1;
101 
102 	return DE_TRUE;
103 }
104 
ContainerStack_pop(ContainerStack * stack)105 DE_INLINE ContainerType ContainerStack_pop (ContainerStack* stack)
106 {
107 	DE_ASSERT(stack->numElements > 0);
108 	stack->numElements -= 1;
109 	return stack->elements[stack->numElements];
110 }
111 
ContainerStack_getTop(const ContainerStack * stack)112 DE_INLINE ContainerType ContainerStack_getTop (const ContainerStack* stack)
113 {
114 	if (stack->numElements > 0)
115 		return stack->elements[stack->numElements-1];
116 	else
117 		return CONTAINERTYPE_LAST;
118 }
119 
120 #endif
121 
122 /* qpTestLog instance */
123 struct qpTestLog_s
124 {
125 	deUint32				flags;				/*!< Logging flags.						*/
126 
127 	deMutex					lock;				/*!< Lock for mutable state below.		*/
128 
129 	/* State protected by lock. */
130 	FILE*					outputFile;
131 	qpXmlWriter*			writer;
132 	deBool					isSessionOpen;
133 	deBool					isCaseOpen;
134 
135 #if defined(DE_DEBUG)
136 	ContainerStack			containerStack;		/*!< For container usage verification.	*/
137 #endif
138 };
139 
140 /* Maps integer to string. */
141 typedef struct qpKeyStringMap_s
142 {
143 	int		key;
144 	char*	string;
145 } qpKeyStringMap;
146 
147 static const char* LOG_FORMAT_VERSION = "0.3.3";
148 
149 /* Mapping enum to above strings... */
150 static const qpKeyStringMap s_qpTestTypeMap[] =
151 {
152 	{ QP_TEST_CASE_TYPE_SELF_VALIDATE,		"SelfValidate"	},
153 	{ QP_TEST_CASE_TYPE_PERFORMANCE,		"Performance"	},
154 	{ QP_TEST_CASE_TYPE_CAPABILITY,			"Capability"	},
155 	{ QP_TEST_CASE_TYPE_ACCURACY,			"Accuracy"		},
156 
157 	{ QP_TEST_CASE_TYPE_LAST,				DE_NULL			}
158 };
159 
160 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTestTypeMap) == QP_TEST_CASE_TYPE_LAST + 1);
161 
162 static const qpKeyStringMap s_qpTestResultMap[] =
163 {
164 	{ QP_TEST_RESULT_PASS,						"Pass"					},
165 	{ QP_TEST_RESULT_FAIL,						"Fail"					},
166 	{ QP_TEST_RESULT_QUALITY_WARNING,			"QualityWarning"		},
167 	{ QP_TEST_RESULT_COMPATIBILITY_WARNING,		"CompatibilityWarning"	},
168 	{ QP_TEST_RESULT_PENDING,					"Pending"				},	/* should not be needed here */
169 	{ QP_TEST_RESULT_NOT_SUPPORTED,				"NotSupported"			},
170 	{ QP_TEST_RESULT_RESOURCE_ERROR,			"ResourceError"			},
171 	{ QP_TEST_RESULT_INTERNAL_ERROR,			"InternalError"			},
172 	{ QP_TEST_RESULT_CRASH,						"Crash"					},
173 	{ QP_TEST_RESULT_TIMEOUT,					"Timeout"				},
174 
175 	/* Add new values here if needed, remember to update qpTestResult enumeration. */
176 
177 	{ QP_TEST_RESULT_LAST,						DE_NULL					}
178 };
179 
180 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTestResultMap) == QP_TEST_RESULT_LAST + 1);
181 
182 /* Key tag to string mapping. */
183 
184 static const qpKeyStringMap s_qpTagMap[] =
185 {
186 	{ QP_KEY_TAG_NONE,			DE_NULL			},
187 	{ QP_KEY_TAG_PERFORMANCE,	"Performance"	},
188 	{ QP_KEY_TAG_QUALITY,		"Quality"		},
189 	{ QP_KEY_TAG_PRECISION,		"Precision"		},
190 	{ QP_KEY_TAG_TIME,			"Time"			},
191 
192 	{ QP_KEY_TAG_LAST,			DE_NULL			}
193 };
194 
195 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTagMap) == QP_KEY_TAG_LAST + 1);
196 
197 /* Sample value tag to string mapping. */
198 
199 static const qpKeyStringMap s_qpSampleValueTagMap[] =
200 {
201 	{ QP_SAMPLE_VALUE_TAG_PREDICTOR,	"Predictor"	},
202 	{ QP_SAMPLE_VALUE_TAG_RESPONSE,		"Response"	},
203 
204 	{ QP_SAMPLE_VALUE_TAG_LAST,			DE_NULL		}
205 };
206 
207 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpSampleValueTagMap) == QP_SAMPLE_VALUE_TAG_LAST + 1);
208 
209 /* Image compression mode to string mapping. */
210 
211 static const qpKeyStringMap s_qpImageCompressionModeMap[] =
212 {
213 	{ QP_IMAGE_COMPRESSION_MODE_NONE,	"None"	},
214 	{ QP_IMAGE_COMPRESSION_MODE_PNG,	"PNG"	},
215 
216 	{ QP_IMAGE_COMPRESSION_MODE_BEST,	DE_NULL	},	/* not allowed to be written! */
217 
218 	{ QP_IMAGE_COMPRESSION_MODE_LAST,	DE_NULL	}
219 };
220 
221 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpImageCompressionModeMap) == QP_IMAGE_COMPRESSION_MODE_LAST + 1);
222 
223 /* Image format to string mapping. */
224 
225 static const qpKeyStringMap s_qpImageFormatMap[] =
226 {
227 	{ QP_IMAGE_FORMAT_RGB888,	"RGB888"	},
228 	{ QP_IMAGE_FORMAT_RGBA8888,	"RGBA8888"	},
229 
230 	{ QP_IMAGE_FORMAT_LAST,		DE_NULL		}
231 };
232 
233 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpImageFormatMap) == QP_IMAGE_FORMAT_LAST + 1);
234 
235 /* Shader type to string mapping. */
236 
237 static const qpKeyStringMap s_qpShaderTypeMap[] =
238 {
239 	{ QP_SHADER_TYPE_VERTEX,			"VertexShader"			},
240 	{ QP_SHADER_TYPE_FRAGMENT,			"FragmentShader"		},
241 	{ QP_SHADER_TYPE_GEOMETRY,			"GeometryShader"		},
242 	{ QP_SHADER_TYPE_TESS_CONTROL,		"TessControlShader"		},
243 	{ QP_SHADER_TYPE_TESS_EVALUATION,	"TessEvaluationShader"	},
244 	{ QP_SHADER_TYPE_COMPUTE,			"ComputeShader"			},
245 
246 	{ QP_SHADER_TYPE_LAST,				DE_NULL					}
247 };
248 
249 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpShaderTypeMap) == QP_SHADER_TYPE_LAST + 1);
250 
qpTestLog_flushFile(qpTestLog * log)251 static void qpTestLog_flushFile (qpTestLog* log)
252 {
253 	DE_ASSERT(log && log->outputFile);
254 	fflush(log->outputFile);
255 #if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
256 	/* \todo [petri] Is this really necessary? */
257 	FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(log->outputFile)));
258 #endif
259 }
260 
261 #define QP_LOOKUP_STRING(KEYMAP, KEY)	qpLookupString(KEYMAP, DE_LENGTH_OF_ARRAY(KEYMAP), (int)(KEY))
262 
qpLookupString(const qpKeyStringMap * keyMap,int keyMapSize,int key)263 static const char* qpLookupString (const qpKeyStringMap* keyMap, int keyMapSize, int key)
264 {
265 	DE_ASSERT(keyMap);
266 	DE_ASSERT(deInBounds32(key, 0, keyMapSize));
267 	DE_ASSERT(keyMap[key].key == key);
268 	DE_UNREF(keyMapSize); /* for asserting only */
269 	return keyMap[key].string;
270 }
271 
int32ToString(int val,char buf[32])272 DE_INLINE void int32ToString (int val, char buf[32])
273 {
274 	deSprintf(&buf[0], 32, "%d", val);
275 }
276 
int64ToString(deInt64 val,char buf[32])277 DE_INLINE void int64ToString (deInt64 val, char buf[32])
278 {
279 	deSprintf(&buf[0], 32, "%lld", (long long int)val);
280 }
281 
floatToString(float value,char * buf,int bufSize)282 DE_INLINE void floatToString (float value, char* buf, int bufSize)
283 {
284 	deSprintf(buf, bufSize, "%f", value);
285 }
286 
doubleToString(double value,char * buf,int bufSize)287 DE_INLINE void doubleToString (double value, char* buf, int bufSize)
288 {
289 	deSprintf(buf, bufSize, "%f", value);
290 }
291 
beginSession(qpTestLog * log)292 static deBool beginSession (qpTestLog* log)
293 {
294 	DE_ASSERT(log && !log->isSessionOpen);
295 
296 	/* Write session info. */
297 	fprintf(log->outputFile, "#sessionInfo releaseName %s\n", qpGetReleaseName());
298 	fprintf(log->outputFile, "#sessionInfo releaseId 0x%08x\n", qpGetReleaseId());
299 	fprintf(log->outputFile, "#sessionInfo targetName \"%s\"\n", qpGetTargetName());
300 
301     /* Write out #beginSession. */
302 	fprintf(log->outputFile, "#beginSession\n");
303 	qpTestLog_flushFile(log);
304 
305 	log->isSessionOpen = DE_TRUE;
306 
307 	return DE_TRUE;
308 }
309 
endSession(qpTestLog * log)310 static deBool endSession (qpTestLog* log)
311 {
312 	DE_ASSERT(log && log->isSessionOpen);
313 
314     /* Make sure xml is flushed. */
315     qpXmlWriter_flush(log->writer);
316 
317     /* Write out #endSession. */
318 	fprintf(log->outputFile, "\n#endSession\n");
319 	qpTestLog_flushFile(log);
320 
321 	log->isSessionOpen = DE_FALSE;
322 
323 	return DE_TRUE;
324 }
325 
326 /*--------------------------------------------------------------------*//*!
327  * \brief Create a file based logger instance
328  * \param fileName Name of the file where to put logs
329  * \return qpTestLog instance, or DE_NULL if cannot create file
330  *//*--------------------------------------------------------------------*/
qpTestLog_createFileLog(const char * fileName,deUint32 flags)331 qpTestLog* qpTestLog_createFileLog (const char* fileName, deUint32 flags)
332 {
333 	qpTestLog* log = (qpTestLog*)deCalloc(sizeof(qpTestLog));
334 	if (!log)
335 		return DE_NULL;
336 
337 	DE_ASSERT(fileName && fileName[0]); /* must have filename. */
338 
339 #if defined(DE_DEBUG)
340 	ContainerStack_reset(&log->containerStack);
341 #endif
342 
343 	/* Create output file. */
344 	log->outputFile = fopen(fileName, "wb");
345 	if (!log->outputFile)
346 	{
347 		qpPrintf("ERROR: Unable to open test log output file '%s'.\n", fileName);
348 		qpTestLog_destroy(log);
349 		return DE_NULL;
350 	}
351 
352 	log->flags			= flags;
353 	log->writer			= qpXmlWriter_createFileWriter(log->outputFile, 0);
354 	log->lock			= deMutex_create(DE_NULL);
355 	log->isSessionOpen	= DE_FALSE;
356 	log->isCaseOpen		= DE_FALSE;
357 
358 	if (!log->writer)
359 	{
360 		qpPrintf("ERROR: Unable to create output XML writer to file '%s'.\n", fileName);
361 		qpTestLog_destroy(log);
362 		return DE_NULL;
363 	}
364 
365 	if (!log->lock)
366 	{
367 		qpPrintf("ERROR: Unable to create mutex.\n");
368 		qpTestLog_destroy(log);
369 		return DE_NULL;
370 	}
371 
372 	beginSession(log);
373 
374 	return log;
375 }
376 
377 /*--------------------------------------------------------------------*//*!
378  * \brief Destroy a logger instance
379  * \param a	qpTestLog instance
380  *//*--------------------------------------------------------------------*/
qpTestLog_destroy(qpTestLog * log)381 void qpTestLog_destroy (qpTestLog* log)
382 {
383 	DE_ASSERT(log);
384 
385 	if (log->isSessionOpen)
386 		endSession(log);
387 
388 	if (log->writer)
389 		qpXmlWriter_destroy(log->writer);
390 
391 	if (log->outputFile)
392 		fclose(log->outputFile);
393 
394 	if (log->lock)
395 		deMutex_destroy(log->lock);
396 
397 	deFree(log);
398 }
399 
400 /*--------------------------------------------------------------------*//*!
401  * \brief Log start of test case
402  * \param log qpTestLog instance
403  * \param testCasePath	Full test case path (as seen in Candy).
404  * \param testCaseType	Test case type
405  * \return true if ok, false otherwise
406  *//*--------------------------------------------------------------------*/
qpTestLog_startCase(qpTestLog * log,const char * testCasePath,qpTestCaseType testCaseType)407 deBool qpTestLog_startCase (qpTestLog* log, const char* testCasePath, qpTestCaseType testCaseType)
408 {
409 	const char*		typeStr				= QP_LOOKUP_STRING(s_qpTestTypeMap, testCaseType);
410 	int				numResultAttribs	= 0;
411 	qpXmlAttribute	resultAttribs[8];
412 
413 	DE_ASSERT(log && testCasePath && (testCasePath[0] != 0));
414 	deMutex_lock(log->lock);
415 
416 	DE_ASSERT(!log->isCaseOpen);
417 	DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
418 
419 	/* Flush XML and write out #beginTestCaseResult. */
420 	qpXmlWriter_flush(log->writer);
421 	fprintf(log->outputFile, "\n#beginTestCaseResult %s\n", testCasePath);
422 	qpTestLog_flushFile(log);
423 
424 	log->isCaseOpen = DE_TRUE;
425 
426 	/* Fill in attributes. */
427 	resultAttribs[numResultAttribs++] = qpSetStringAttrib("Version", LOG_FORMAT_VERSION);
428 	resultAttribs[numResultAttribs++] = qpSetStringAttrib("CasePath", testCasePath);
429 	resultAttribs[numResultAttribs++] = qpSetStringAttrib("CaseType", typeStr);
430 
431 	if (!qpXmlWriter_startDocument(log->writer) ||
432 		!qpXmlWriter_startElement(log->writer, "TestCaseResult", numResultAttribs, resultAttribs))
433 	{
434 		qpPrintf("qpTestLog_startCase(): Writing XML failed\n");
435 		deMutex_unlock(log->lock);
436 		return DE_FALSE;
437 	}
438 
439 	deMutex_unlock(log->lock);
440 	return DE_TRUE;
441 }
442 
443 /*--------------------------------------------------------------------*//*!
444  * \brief Log end of test case
445  * \param log qpTestLog instance
446  * \param result Test result
447  * \param description Description of a problem in case of error
448  * \return true if ok, false otherwise
449  *//*--------------------------------------------------------------------*/
qpTestLog_endCase(qpTestLog * log,qpTestResult result,const char * resultDetails)450 deBool qpTestLog_endCase (qpTestLog* log, qpTestResult result, const char* resultDetails)
451 {
452 	const char*		statusStr		= QP_LOOKUP_STRING(s_qpTestResultMap, result);
453 	qpXmlAttribute	statusAttrib	= qpSetStringAttrib("StatusCode", statusStr);
454 
455 	DE_ASSERT(log && log->isCaseOpen);
456 	DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
457 	deMutex_lock(log->lock);
458 
459 	/* <Result StatusCode="Pass">Result details</Result>
460 	 * </TestCaseResult>
461 	 */
462 	if (!qpXmlWriter_startElement(log->writer, "Result", 1, &statusAttrib) ||
463 		(resultDetails && !qpXmlWriter_writeString(log->writer, resultDetails)) ||
464 		!qpXmlWriter_endElement(log->writer, "Result") ||
465 		!qpXmlWriter_endElement(log->writer, "TestCaseResult") ||
466 		!qpXmlWriter_endDocument(log->writer))		/* Close any XML elements still open */
467 	{
468 		qpPrintf("qpTestLog_endCase(): Writing XML failed\n");
469 		deMutex_unlock(log->lock);
470 		return DE_FALSE;
471 	}
472 
473 	/* Flush XML and write #endTestCaseResult. */
474 	qpXmlWriter_flush(log->writer);
475 	fprintf(log->outputFile, "\n#endTestCaseResult\n");
476 	qpTestLog_flushFile(log);
477 
478 	log->isCaseOpen = DE_FALSE;
479 
480 	deMutex_unlock(log->lock);
481 	return DE_TRUE;
482 }
483 
484 /*--------------------------------------------------------------------*//*!
485  * \brief Abrupt termination of logging.
486  * \param log		qpTestLog instance
487  * \param result	Result code, only Crash and Timeout are allowed.
488  * \return true if ok, false otherwise
489  *//*--------------------------------------------------------------------*/
qpTestLog_terminateCase(qpTestLog * log,qpTestResult result)490 deBool qpTestLog_terminateCase (qpTestLog* log, qpTestResult result)
491 {
492 	const char* resultStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
493 
494 	DE_ASSERT(log);
495 	DE_ASSERT(result == QP_TEST_RESULT_CRASH || result == QP_TEST_RESULT_TIMEOUT);
496 
497 	deMutex_lock(log->lock);
498 
499 	if (!log->isCaseOpen)
500 	{
501 		deMutex_unlock(log->lock);
502 		return DE_FALSE; /* Soft error. This is called from error handler. */
503 	}
504 
505 	/* Flush XML and write #terminateTestCaseResult. */
506 	qpXmlWriter_flush(log->writer);
507 	fprintf(log->outputFile, "\n#terminateTestCaseResult %s\n", resultStr);
508 	qpTestLog_flushFile(log);
509 
510 	log->isCaseOpen = DE_FALSE;
511 
512 #if defined(DE_DEBUG)
513 	ContainerStack_reset(&log->containerStack);
514 #endif
515 
516 	deMutex_unlock(log->lock);
517 	return DE_TRUE;
518 }
519 
qpTestLog_writeKeyValuePair(qpTestLog * log,const char * elementName,const char * name,const char * description,const char * unit,qpKeyValueTag tag,const char * text)520 static deBool qpTestLog_writeKeyValuePair (qpTestLog* log, const char* elementName, const char* name, const char* description, const char* unit, qpKeyValueTag tag, const char* text)
521 {
522 	const char*		tagString = QP_LOOKUP_STRING(s_qpTagMap, tag);
523 	qpXmlAttribute	attribs[8];
524 	int				numAttribs = 0;
525 
526 	DE_ASSERT(log && elementName && text);
527 	deMutex_lock(log->lock);
528 
529 	/* Fill in attributes. */
530 	if (name)			attribs[numAttribs++] = qpSetStringAttrib("Name", name);
531 	if (description)	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
532 	if (tagString)		attribs[numAttribs++] = qpSetStringAttrib("Tag", tagString);
533 	if (unit)			attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
534 
535 	if (!qpXmlWriter_startElement(log->writer, elementName, numAttribs, attribs) ||
536 		!qpXmlWriter_writeString(log->writer, text) ||
537 		!qpXmlWriter_endElement(log->writer, elementName))
538 	{
539 		qpPrintf("qpTestLog_writeKeyValuePair(): Writing XML failed\n");
540 		deMutex_unlock(log->lock);
541 		return DE_FALSE;
542 	}
543 
544 	deMutex_unlock(log->lock);
545 	return DE_TRUE;
546 }
547 
548 /*--------------------------------------------------------------------*//*!
549  * \brief Write a message to output log
550  * \param log		qpTestLog instance
551  * \param format	Format string of message
552  * \param ...		Parameters for message
553  * \return true if ok, false otherwise
554  *//*--------------------------------------------------------------------*/
qpTestLog_writeMessage(qpTestLog * log,const char * format,...)555 deBool qpTestLog_writeMessage (qpTestLog* log, const char* format, ...)
556 {
557 	char	buffer[1024];
558 	va_list	args;
559 
560 	/* \todo [petri] Handle buffer overflows! */
561 
562 	va_start(args, format);
563 	buffer[DE_LENGTH_OF_ARRAY(buffer) - 1] = 0;
564 	vsnprintf(buffer, sizeof(buffer), format, args);
565 	va_end(args);
566 
567 	printf("%s\n", buffer);
568 
569 	/* <Text>text</Text> */
570 	return qpTestLog_writeKeyValuePair(log, "Text", DE_NULL, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, buffer);
571 }
572 
573 /*--------------------------------------------------------------------*//*!
574  * \brief Write key-value-pair into log
575  * \param log			qpTestLog instance
576  * \param name			Unique identifier for entry
577  * \param description	Human readable description
578  * \param tag			Optional tag
579  * \param value			Value of the key-value-pair
580  * \return true if ok, false otherwise
581  *//*--------------------------------------------------------------------*/
qpTestLog_writeText(qpTestLog * log,const char * name,const char * description,qpKeyValueTag tag,const char * text)582 deBool qpTestLog_writeText (qpTestLog* log, const char* name, const char* description, qpKeyValueTag tag, const char* text)
583 {
584 	/* <Text Name="name" Description="description" Tag="tag">text</Text> */
585 	return qpTestLog_writeKeyValuePair(log, "Text", name, description, DE_NULL, tag, text);
586 }
587 
588 /*--------------------------------------------------------------------*//*!
589  * \brief Write key-value-pair into log
590  * \param log			qpTestLog instance
591  * \param name			Unique identifier for entry
592  * \param description	Human readable description
593  * \param tag			Optional tag
594  * \param value			Value of the key-value-pair
595  * \return true if ok, false otherwise
596  *//*--------------------------------------------------------------------*/
qpTestLog_writeInteger(qpTestLog * log,const char * name,const char * description,const char * unit,qpKeyValueTag tag,deInt64 value)597 deBool qpTestLog_writeInteger (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
598 {
599 	char tmpString[64];
600 	int64ToString(value, tmpString);
601 
602 	printf("%s = %lld %s\n", description, (signed long long)value, unit ? unit : "");
603 
604 	/* <Number Name="name" Description="description" Tag="Performance">15</Number> */
605 	return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
606 }
607 
608 /*--------------------------------------------------------------------*//*!
609  * \brief Write key-value-pair into log
610  * \param log			qpTestLog instance
611  * \param name			Unique identifier for entry
612  * \param description	Human readable description
613  * \param tag			Optional tag
614  * \param value			Value of the key-value-pair
615  * \return true if ok, false otherwise
616  *//*--------------------------------------------------------------------*/
qpTestLog_writeFloat(qpTestLog * log,const char * name,const char * description,const char * unit,qpKeyValueTag tag,float value)617 deBool qpTestLog_writeFloat (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
618 {
619 	char tmpString[64];
620 	floatToString(value, tmpString, sizeof(tmpString));
621 
622 	printf("%s = %f %s\n", description, value, unit ? unit : "");
623 
624 	/* <Number Name="name" Description="description" Tag="Performance">15</Number> */
625 	return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
626 }
627 
628 typedef struct Buffer_s
629 {
630 	int			capacity;
631 	int			size;
632 	deUint8*	data;
633 } Buffer;
634 
Buffer_init(Buffer * buffer)635 void Buffer_init (Buffer* buffer)
636 {
637 	buffer->capacity	= 0;
638 	buffer->size		= 0;
639 	buffer->data		= DE_NULL;
640 }
641 
Buffer_deinit(Buffer * buffer)642 void Buffer_deinit (Buffer* buffer)
643 {
644 	deFree(buffer->data);
645 	Buffer_init(buffer);
646 }
647 
Buffer_resize(Buffer * buffer,int newSize)648 deBool Buffer_resize (Buffer* buffer, int newSize)
649 {
650 	/* Grow buffer if necessary. */
651 	if (newSize > buffer->capacity)
652 	{
653 		int			newCapacity	= deAlign32(deMax32(2*buffer->capacity, newSize), 512);
654 		deUint8*	newData		= (deUint8*)deMalloc(newCapacity);
655 		if (!newData)
656 			return DE_FALSE;
657 
658 		memcpy(newData, buffer->data, buffer->size);
659 		deFree(buffer->data);
660 		buffer->data		= newData;
661 		buffer->capacity	= newCapacity;
662 	}
663 
664 	buffer->size = newSize;
665 	return DE_TRUE;
666 }
667 
Buffer_append(Buffer * buffer,const deUint8 * data,int numBytes)668 deBool Buffer_append (Buffer* buffer, const deUint8* data, int numBytes)
669 {
670 	int offset = buffer->size;
671 
672 	if (!Buffer_resize(buffer, buffer->size + numBytes))
673 		return DE_FALSE;
674 
675 	/* Append bytes. */
676 	memcpy(&buffer->data[offset], data, numBytes);
677 	return DE_TRUE;
678 }
679 
680 #if defined(QP_SUPPORT_PNG)
pngWriteData(png_structp png,png_bytep dataPtr,png_size_t numBytes)681 void pngWriteData (png_structp png, png_bytep dataPtr, png_size_t numBytes)
682 {
683 	Buffer* buffer = (Buffer*)png_get_io_ptr(png);
684 	if (!Buffer_append(buffer, (const deUint8*)dataPtr, (int)numBytes))
685 		png_error(png, "unable to resize PNG write buffer!");
686 }
687 
pngFlushData(png_structp png)688 void pngFlushData (png_structp png)
689 {
690 	DE_UNREF(png);
691 	/* nada */
692 }
693 
writeCompressedPNG(png_structp png,png_infop info,png_byte ** rowPointers,int width,int height,int colorFormat)694 static deBool writeCompressedPNG (png_structp png, png_infop info, png_byte** rowPointers, int width, int height, int colorFormat)
695 {
696 	if (setjmp(png_jmpbuf(png)) == 0)
697 	{
698 		/* Write data. */
699 		png_set_IHDR(png, info, width, height,
700 			8,
701 			colorFormat,
702 			PNG_INTERLACE_NONE,
703 			PNG_COMPRESSION_TYPE_BASE,
704 			PNG_FILTER_TYPE_BASE);
705 		png_write_info(png, info);
706 		png_write_image(png, rowPointers);
707 		png_write_end(png, NULL);
708 
709 		return DE_TRUE;
710 	}
711 	else
712 		return DE_FALSE;
713 }
714 
compressImagePNG(Buffer * buffer,qpImageFormat imageFormat,int width,int height,int rowStride,const void * data)715 static deBool compressImagePNG (Buffer* buffer, qpImageFormat imageFormat, int width, int height, int rowStride, const void* data)
716 {
717 	deBool			compressOk		= DE_FALSE;
718 	png_structp		png				= DE_NULL;
719 	png_infop		info			= DE_NULL;
720 	png_byte**		rowPointers		= DE_NULL;
721 	deBool			hasAlpha		= imageFormat == QP_IMAGE_FORMAT_RGBA8888;
722 	int				ndx;
723 
724 	/* Handle format. */
725 	DE_ASSERT(imageFormat == QP_IMAGE_FORMAT_RGB888 || imageFormat == QP_IMAGE_FORMAT_RGBA8888);
726 
727 	/* Allocate & set row pointers. */
728 	rowPointers = (png_byte**)deMalloc(height * sizeof(png_byte*));
729 	if (!rowPointers)
730 		return DE_FALSE;
731 
732 	for (ndx = 0; ndx < height; ndx++)
733 		rowPointers[ndx] = (png_byte*)((const deUint8*)data + ndx*rowStride);
734 
735 	/* Initialize PNG compressor. */
736 	png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
737 	info = png ? png_create_info_struct(png) : DE_NULL;
738 	if (png && info)
739 	{
740 		/* Set our own write function. */
741 		png_set_write_fn(png, buffer, pngWriteData, pngFlushData);
742 
743 		compressOk = writeCompressedPNG(png, info, rowPointers, width, height,
744 										hasAlpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
745 	}
746 
747 	/* Cleanup & return. */
748 	if (png && info)
749 	{
750 		png_destroy_info_struct(png, &info);
751 		png_destroy_write_struct(&png, DE_NULL);
752 	}
753 	else if (png)
754 		png_destroy_write_struct(&png, &info);
755 
756 	deFree(rowPointers);
757 	return compressOk;
758 }
759 #endif /* QP_SUPPORT_PNG */
760 
761 /*--------------------------------------------------------------------*//*!
762  * \brief Start image set
763  * \param log			qpTestLog instance
764  * \param name			Unique identifier for the set
765  * \param description	Human readable description
766  * \return true if ok, false otherwise
767  *//*--------------------------------------------------------------------*/
qpTestLog_startImageSet(qpTestLog * log,const char * name,const char * description)768 deBool qpTestLog_startImageSet (qpTestLog* log, const char* name, const char* description)
769 {
770 	qpXmlAttribute	attribs[4];
771 	int				numAttribs = 0;
772 
773 	DE_ASSERT(log && name);
774 	deMutex_lock(log->lock);
775 
776 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
777 	if (description)
778 		attribs[numAttribs++] = qpSetStringAttrib("Description", description);
779 
780 	/* <ImageSet Name="<name>"> */
781 	if (!qpXmlWriter_startElement(log->writer, "ImageSet", numAttribs, attribs))
782 	{
783 		qpPrintf("qpTestLog_startImageSet(): Writing XML failed\n");
784 		deMutex_unlock(log->lock);
785 		return DE_FALSE;
786 	}
787 
788 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_IMAGESET));
789 
790 	deMutex_unlock(log->lock);
791 	return DE_TRUE;
792 }
793 
794 /*--------------------------------------------------------------------*//*!
795  * \brief End image set
796  * \param log			qpTestLog instance
797  * \return true if ok, false otherwise
798  *//*--------------------------------------------------------------------*/
qpTestLog_endImageSet(qpTestLog * log)799 deBool qpTestLog_endImageSet (qpTestLog* log)
800 {
801 	DE_ASSERT(log);
802 	deMutex_lock(log->lock);
803 
804 	/* <ImageSet Name="<name>"> */
805 	if (!qpXmlWriter_endElement(log->writer, "ImageSet"))
806 	{
807 		qpPrintf("qpTestLog_endImageSet(): Writing XML failed\n");
808 		deMutex_unlock(log->lock);
809 		return DE_FALSE;
810 	}
811 
812 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_IMAGESET);
813 
814 	deMutex_unlock(log->lock);
815 	return DE_TRUE;
816 }
817 
818 /*--------------------------------------------------------------------*//*!
819  * \brief Write base64 encoded raw image data into log
820  * \param log				qpTestLog instance
821  * \param name				Unique name (matching names can be compared across BatchResults).
822  * \param description		Textual description (shown in Candy).
823  * \param compressionMode	Compression mode
824  * \param imageFormat		Color format
825  * \param width				Width in pixels
826  * \param height			Height in pixels
827  * \param stride			Data stride (offset between rows)
828  * \param data				Pointer to pixel data
829  * \return 0 if OK, otherwise <0
830  *//*--------------------------------------------------------------------*/
qpTestLog_writeImage(qpTestLog * log,const char * name,const char * description,qpImageCompressionMode compressionMode,qpImageFormat imageFormat,int width,int height,int stride,const void * data)831 deBool qpTestLog_writeImage	(
832 	qpTestLog*				log,
833 	const char*				name,
834 	const char*				description,
835 	qpImageCompressionMode	compressionMode,
836 	qpImageFormat			imageFormat,
837 	int						width,
838 	int						height,
839 	int						stride,
840 	const void*				data)
841 {
842 	char			widthStr[32];
843 	char			heightStr[32];
844 	qpXmlAttribute	attribs[8];
845 	int				numAttribs			= 0;
846 	Buffer			compressedBuffer;
847 	const void*		writeDataPtr		= DE_NULL;
848 	int				writeDataBytes		= -1;
849 
850 	DE_ASSERT(log && name);
851 	DE_ASSERT(deInRange32(width, 1, 16384));
852 	DE_ASSERT(deInRange32(height, 1, 16384));
853 	DE_ASSERT(data);
854 
855 	if (log->flags & QP_TEST_LOG_EXCLUDE_IMAGES)
856 		return DE_TRUE; /* Image not logged. */
857 
858 	Buffer_init(&compressedBuffer);
859 
860 	/* BEST compression mode defaults to PNG. */
861 	if (compressionMode == QP_IMAGE_COMPRESSION_MODE_BEST)
862 	{
863 #if defined(QP_SUPPORT_PNG)
864 		compressionMode = QP_IMAGE_COMPRESSION_MODE_PNG;
865 #else
866 		compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
867 #endif
868 	}
869 
870 #if defined(QP_SUPPORT_PNG)
871 	/* Try storing with PNG compression. */
872 	if (compressionMode == QP_IMAGE_COMPRESSION_MODE_PNG)
873 	{
874 		deBool compressOk = compressImagePNG(&compressedBuffer, imageFormat, width, height, stride, data);
875 		if (compressOk)
876 		{
877 			writeDataPtr	= compressedBuffer.data;
878 			writeDataBytes	= compressedBuffer.size;
879 		}
880 		else
881 		{
882 			/* Fall-back to default compression. */
883 			qpPrintf("WARNING: PNG compression failed -- storing image uncompressed.\n");
884 			compressionMode	= QP_IMAGE_COMPRESSION_MODE_NONE;
885 		}
886 	}
887 #endif
888 
889 	/* Handle image compression. */
890 	switch (compressionMode)
891 	{
892 		case QP_IMAGE_COMPRESSION_MODE_NONE:
893 		{
894 			int pixelSize		= imageFormat == QP_IMAGE_FORMAT_RGB888 ? 3 : 4;
895 			int packedStride	= pixelSize*width;
896 
897 			if (packedStride == stride)
898 				writeDataPtr = data;
899 			else
900 			{
901 				/* Need to re-pack pixels. */
902 				if (Buffer_resize(&compressedBuffer, packedStride*height))
903 				{
904 					int row;
905 					for (row = 0; row < height; row++)
906 						memcpy(&compressedBuffer.data[packedStride*row], &((const deUint8*)data)[row*stride], pixelSize*width);
907 				}
908 				else
909 				{
910 					qpPrintf("ERROR: Failed to pack pixels for writing.\n");
911 					Buffer_deinit(&compressedBuffer);
912 					return DE_FALSE;
913 				}
914 			}
915 
916 			writeDataBytes = packedStride*height;
917 			break;
918 		}
919 
920 #if defined(QP_SUPPORT_PNG)
921 		case QP_IMAGE_COMPRESSION_MODE_PNG:
922 			DE_ASSERT(writeDataPtr); /* Already handled. */
923 			break;
924 #endif
925 
926 		default:
927 			qpPrintf("qpTestLog_writeImage(): Unknown compression mode: %s\n", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
928 			Buffer_deinit(&compressedBuffer);
929 			return DE_FALSE;
930 	}
931 
932 	/* Fill in attributes. */
933 	int32ToString(width, widthStr);
934 	int32ToString(height, heightStr);
935 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
936 	attribs[numAttribs++] = qpSetStringAttrib("Width", widthStr);
937 	attribs[numAttribs++] = qpSetStringAttrib("Height", heightStr);
938 	attribs[numAttribs++] = qpSetStringAttrib("Format", QP_LOOKUP_STRING(s_qpImageFormatMap, imageFormat));
939 	attribs[numAttribs++] = qpSetStringAttrib("CompressionMode", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
940 	if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
941 
942 	/* \note Log lock is acquired after compression! */
943 	deMutex_lock(log->lock);
944 
945 	/* <Image ID="result" Name="Foobar" Width="640" Height="480" Format="RGB888" CompressionMode="None">base64 data</Image> */
946 	if (!qpXmlWriter_startElement(log->writer, "Image", numAttribs, attribs) ||
947 		!qpXmlWriter_writeBase64(log->writer, (const deUint8*)writeDataPtr, writeDataBytes) ||
948 		!qpXmlWriter_endElement(log->writer, "Image"))
949 	{
950 		qpPrintf("qpTestLog_writeImage(): Writing XML failed\n");
951 		deMutex_unlock(log->lock);
952 		Buffer_deinit(&compressedBuffer);
953 		return DE_FALSE;
954 	}
955 
956 	deMutex_unlock(log->lock);
957 
958 	/* Free compressed data if allocated. */
959 	Buffer_deinit(&compressedBuffer);
960 
961 	return DE_TRUE;
962 }
963 
964 /*--------------------------------------------------------------------*//*!
965  * \brief Write a OpenGL ES shader program into the log.
966  * \param linkOk			Shader program link result, false on failure
967  * \param linkInfoLog		Implementation provided linkage log
968  *//*--------------------------------------------------------------------*/
qpTestLog_startShaderProgram(qpTestLog * log,deBool linkOk,const char * linkInfoLog)969 deBool qpTestLog_startShaderProgram (qpTestLog* log, deBool linkOk, const char* linkInfoLog)
970 {
971 	qpXmlAttribute	programAttribs[4];
972 	int				numProgramAttribs = 0;
973 
974 	DE_ASSERT(log);
975 	deMutex_lock(log->lock);
976 
977 	programAttribs[numProgramAttribs++] = qpSetStringAttrib("LinkStatus", linkOk ? "OK" : "Fail");
978 
979 	if (!qpXmlWriter_startElement(log->writer, "ShaderProgram", numProgramAttribs, programAttribs) ||
980 		!qpXmlWriter_writeStringElement(log->writer, "InfoLog", linkInfoLog))
981 	{
982 		qpPrintf("qpTestLog_startShaderProgram(): Writing XML failed\n");
983 		deMutex_unlock(log->lock);
984 		return DE_FALSE;
985 	}
986 
987 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SHADERPROGRAM));
988 
989 	deMutex_unlock(log->lock);
990 	return DE_TRUE;
991 }
992 
993 /*--------------------------------------------------------------------*//*!
994  * \brief End shader program
995  * \param log			qpTestLog instance
996  * \return true if ok, false otherwise
997  *//*--------------------------------------------------------------------*/
qpTestLog_endShaderProgram(qpTestLog * log)998 deBool qpTestLog_endShaderProgram (qpTestLog* log)
999 {
1000 	DE_ASSERT(log);
1001 	deMutex_lock(log->lock);
1002 
1003 	/* </ShaderProgram> */
1004 	if (!qpXmlWriter_endElement(log->writer, "ShaderProgram"))
1005 	{
1006 		qpPrintf("qpTestLog_endShaderProgram(): Writing XML failed\n");
1007 		deMutex_unlock(log->lock);
1008 		return DE_FALSE;
1009 	}
1010 
1011 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1012 
1013 	deMutex_unlock(log->lock);
1014 	return DE_TRUE;
1015 }
1016 
1017 /*--------------------------------------------------------------------*//*!
1018  * \brief Write a OpenGL ES shader into the log.
1019  * \param type				Shader type
1020  * \param source	 		Shader source
1021  * \param compileOk			Shader compilation result, false on failure
1022  * \param infoLog			Implementation provided shader compilation log
1023  *//*--------------------------------------------------------------------*/
qpTestLog_writeShader(qpTestLog * log,qpShaderType type,const char * source,deBool compileOk,const char * infoLog)1024 deBool qpTestLog_writeShader (qpTestLog* log, qpShaderType type, const char* source, deBool compileOk, const char* infoLog)
1025 {
1026 	const char*		tagName				= QP_LOOKUP_STRING(s_qpShaderTypeMap, type);
1027 	int				numShaderAttribs	= 0;
1028 	qpXmlAttribute	shaderAttribs[4];
1029 
1030 	DE_ASSERT(log && source);
1031 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1032 	deMutex_lock(log->lock);
1033 
1034 	shaderAttribs[numShaderAttribs++]	= qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1035 
1036 	if (!qpXmlWriter_startElement(log->writer, tagName, numShaderAttribs, shaderAttribs) ||
1037 		!qpXmlWriter_writeStringElement(log->writer, "ShaderSource", source) ||
1038 		!qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1039 		!qpXmlWriter_endElement(log->writer, tagName))
1040 	{
1041 		qpPrintf("qpTestLog_writeShader(): Writing XML failed\n");
1042 		deMutex_unlock(log->lock);
1043 		return DE_FALSE;
1044 	}
1045 
1046 	deMutex_unlock(log->lock);
1047 	return DE_TRUE;
1048 }
1049 
1050 /*--------------------------------------------------------------------*//*!
1051  * \brief Start writing a set of EGL configurations into the log.
1052  *//*--------------------------------------------------------------------*/
qpTestLog_startEglConfigSet(qpTestLog * log,const char * name,const char * description)1053 deBool qpTestLog_startEglConfigSet (qpTestLog* log, const char* name, const char* description)
1054 {
1055 	qpXmlAttribute	attribs[4];
1056 	int				numAttribs = 0;
1057 
1058 	DE_ASSERT(log && name);
1059 	deMutex_lock(log->lock);
1060 
1061 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1062 	if (description)
1063 		attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1064 
1065 	/* <EglConfigSet Name="<name>"> */
1066 	if (!qpXmlWriter_startElement(log->writer, "EglConfigSet", numAttribs, attribs))
1067 	{
1068 		qpPrintf("qpTestLog_startEglImageSet(): Writing XML failed\n");
1069 		deMutex_unlock(log->lock);
1070 		return DE_FALSE;
1071 	}
1072 
1073 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_EGLCONFIGSET));
1074 
1075 	deMutex_unlock(log->lock);
1076 	return DE_TRUE;
1077 }
1078 
1079 /*--------------------------------------------------------------------*//*!
1080  * \brief End an EGL config set
1081  *//*--------------------------------------------------------------------*/
qpTestLog_endEglConfigSet(qpTestLog * log)1082 deBool qpTestLog_endEglConfigSet (qpTestLog* log)
1083 {
1084 	DE_ASSERT(log);
1085 	deMutex_lock(log->lock);
1086 
1087 	/* <EglConfigSet Name="<name>"> */
1088 	if (!qpXmlWriter_endElement(log->writer, "EglConfigSet"))
1089 	{
1090 		qpPrintf("qpTestLog_endEglImageSet(): Writing XML failed\n");
1091 		deMutex_unlock(log->lock);
1092 		return DE_FALSE;
1093 	}
1094 
1095 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_EGLCONFIGSET);
1096 
1097 	deMutex_unlock(log->lock);
1098 	return DE_TRUE;
1099 }
1100 
1101 /*--------------------------------------------------------------------*//*!
1102  * \brief Write an EGL config inside an EGL config set
1103  * \see   qpElgConfigInfo for details
1104  *//*--------------------------------------------------------------------*/
qpTestLog_writeEglConfig(qpTestLog * log,const qpEglConfigInfo * config)1105 deBool qpTestLog_writeEglConfig (qpTestLog* log, const qpEglConfigInfo* config)
1106 {
1107 	qpXmlAttribute	attribs[64];
1108 	int				numAttribs = 0;
1109 
1110 	DE_ASSERT(log && config);
1111 	deMutex_lock(log->lock);
1112 
1113 	attribs[numAttribs++] = qpSetIntAttrib		("BufferSize", config->bufferSize);
1114 	attribs[numAttribs++] = qpSetIntAttrib		("RedSize", config->redSize);
1115 	attribs[numAttribs++] = qpSetIntAttrib		("GreenSize", config->greenSize);
1116 	attribs[numAttribs++] = qpSetIntAttrib		("BlueSize", config->blueSize);
1117 	attribs[numAttribs++] = qpSetIntAttrib		("LuminanceSize", config->luminanceSize);
1118 	attribs[numAttribs++] = qpSetIntAttrib		("AlphaSize", config->alphaSize);
1119 	attribs[numAttribs++] = qpSetIntAttrib		("AlphaMaskSize", config->alphaMaskSize);
1120 	attribs[numAttribs++] = qpSetBoolAttrib		("BindToTextureRGB", config->bindToTextureRGB);
1121 	attribs[numAttribs++] = qpSetBoolAttrib		("BindToTextureRGBA", config->bindToTextureRGBA);
1122 	attribs[numAttribs++] = qpSetStringAttrib	("ColorBufferType", config->colorBufferType);
1123 	attribs[numAttribs++] = qpSetStringAttrib	("ConfigCaveat", config->configCaveat);
1124 	attribs[numAttribs++] = qpSetIntAttrib		("ConfigID", config->configID);
1125 	attribs[numAttribs++] = qpSetStringAttrib	("Conformant", config->conformant);
1126 	attribs[numAttribs++] = qpSetIntAttrib		("DepthSize", config->depthSize);
1127 	attribs[numAttribs++] = qpSetIntAttrib		("Level", config->level);
1128 	attribs[numAttribs++] = qpSetIntAttrib		("MaxPBufferWidth", config->maxPBufferWidth);
1129 	attribs[numAttribs++] = qpSetIntAttrib		("MaxPBufferHeight", config->maxPBufferHeight);
1130 	attribs[numAttribs++] = qpSetIntAttrib		("MaxPBufferPixels", config->maxPBufferPixels);
1131 	attribs[numAttribs++] = qpSetIntAttrib		("MaxSwapInterval", config->maxSwapInterval);
1132 	attribs[numAttribs++] = qpSetIntAttrib		("MinSwapInterval", config->minSwapInterval);
1133 	attribs[numAttribs++] = qpSetBoolAttrib		("NativeRenderable", config->nativeRenderable);
1134 	attribs[numAttribs++] = qpSetStringAttrib	("RenderableType", config->renderableType);
1135 	attribs[numAttribs++] = qpSetIntAttrib		("SampleBuffers", config->sampleBuffers);
1136 	attribs[numAttribs++] = qpSetIntAttrib		("Samples", config->samples);
1137 	attribs[numAttribs++] = qpSetIntAttrib		("StencilSize", config->stencilSize);
1138 	attribs[numAttribs++] = qpSetStringAttrib	("SurfaceTypes", config->surfaceTypes);
1139 	attribs[numAttribs++] = qpSetStringAttrib	("TransparentType", config->transparentType);
1140 	attribs[numAttribs++] = qpSetIntAttrib		("TransparentRedValue", config->transparentRedValue);
1141 	attribs[numAttribs++] = qpSetIntAttrib		("TransparentGreenValue", config->transparentGreenValue);
1142 	attribs[numAttribs++] = qpSetIntAttrib		("TransparentBlueValue", config->transparentBlueValue);
1143 	DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
1144 
1145 	if (!qpXmlWriter_startElement(log->writer, "EglConfig", numAttribs, attribs) ||
1146 		!qpXmlWriter_endElement(log->writer, "EglConfig"))
1147 	{
1148 		qpPrintf("qpTestLog_writeEglConfig(): Writing XML failed\n");
1149 		deMutex_unlock(log->lock);
1150 		return DE_FALSE;
1151 	}
1152 
1153 	deMutex_unlock(log->lock);
1154 	return DE_TRUE;
1155 }
1156 
1157 /*--------------------------------------------------------------------*//*!
1158  * \brief Start section in log.
1159  * \param log			qpTestLog instance
1160  * \param name			Section name
1161  * \param description	Human readable description
1162  * \return true if ok, false otherwise
1163  *//*--------------------------------------------------------------------*/
qpTestLog_startSection(qpTestLog * log,const char * name,const char * description)1164 deBool qpTestLog_startSection (qpTestLog* log, const char* name, const char* description)
1165 {
1166 	qpXmlAttribute	attribs[2];
1167 	int				numAttribs = 0;
1168 
1169 	DE_ASSERT(log && name);
1170 	deMutex_lock(log->lock);
1171 
1172 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1173 	if (description)
1174 		attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1175 
1176 	/* <Section Name="<name>" Description="<description>"> */
1177 	if (!qpXmlWriter_startElement(log->writer, "Section", numAttribs, attribs))
1178 	{
1179 		qpPrintf("qpTestLog_startSection(): Writing XML failed\n");
1180 		deMutex_unlock(log->lock);
1181 		return DE_FALSE;
1182 	}
1183 
1184 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SECTION));
1185 
1186 	deMutex_unlock(log->lock);
1187 	return DE_TRUE;
1188 }
1189 
1190 /*--------------------------------------------------------------------*//*!
1191  * \brief End section in log.
1192  * \param log			qpTestLog instance
1193  * \return true if ok, false otherwise
1194  *//*--------------------------------------------------------------------*/
qpTestLog_endSection(qpTestLog * log)1195 deBool qpTestLog_endSection (qpTestLog* log)
1196 {
1197 	DE_ASSERT(log);
1198 	deMutex_lock(log->lock);
1199 
1200 	/* </Section> */
1201 	if (!qpXmlWriter_endElement(log->writer, "Section"))
1202 	{
1203 		qpPrintf("qpTestLog_endSection(): Writing XML failed\n");
1204 		deMutex_unlock(log->lock);
1205 		return DE_FALSE;
1206 	}
1207 
1208 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SECTION);
1209 
1210 	deMutex_unlock(log->lock);
1211 	return DE_TRUE;
1212 }
1213 
1214 /*--------------------------------------------------------------------*//*!
1215  * \brief Write OpenCL compute kernel source into the log.
1216  *//*--------------------------------------------------------------------*/
qpTestLog_writeKernelSource(qpTestLog * log,const char * source)1217 deBool qpTestLog_writeKernelSource (qpTestLog* log, const char* source)
1218 {
1219 	DE_ASSERT(log);
1220 	deMutex_lock(log->lock);
1221 
1222 	if (!qpXmlWriter_writeStringElement(log->writer, "KernelSource", source))
1223 	{
1224 		qpPrintf("qpTestLog_writeKernelSource(): Writing XML failed\n");
1225 		deMutex_unlock(log->lock);
1226 		return DE_FALSE;
1227 	}
1228 
1229 	deMutex_unlock(log->lock);
1230 	return DE_TRUE;
1231 }
1232 
1233 /*--------------------------------------------------------------------*//*!
1234  * \brief Write OpenCL kernel compilation results into the log
1235  *//*--------------------------------------------------------------------*/
qpTestLog_writeCompileInfo(qpTestLog * log,const char * name,const char * description,deBool compileOk,const char * infoLog)1236 deBool qpTestLog_writeCompileInfo (qpTestLog* log, const char* name, const char* description, deBool compileOk, const char* infoLog)
1237 {
1238 	int				numAttribs = 0;
1239 	qpXmlAttribute	attribs[3];
1240 
1241 	DE_ASSERT(log && name && description && infoLog);
1242 	deMutex_lock(log->lock);
1243 
1244 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1245 	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1246 	attribs[numAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1247 
1248 	if (!qpXmlWriter_startElement(log->writer, "CompileInfo", numAttribs, attribs) ||
1249 		!qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1250 		!qpXmlWriter_endElement(log->writer, "CompileInfo"))
1251 	{
1252 		qpPrintf("qpTestLog_writeCompileInfo(): Writing XML failed\n");
1253 		deMutex_unlock(log->lock);
1254 		return DE_FALSE;
1255 	}
1256 
1257 	deMutex_unlock(log->lock);
1258 	return DE_TRUE;
1259 }
1260 
qpTestLog_startSampleList(qpTestLog * log,const char * name,const char * description)1261 deBool qpTestLog_startSampleList (qpTestLog* log, const char* name, const char* description)
1262 {
1263 	int				numAttribs = 0;
1264 	qpXmlAttribute	attribs[2];
1265 
1266 	DE_ASSERT(log && name && description);
1267 	deMutex_lock(log->lock);
1268 
1269 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1270 	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1271 
1272 	if (!qpXmlWriter_startElement(log->writer, "SampleList", numAttribs, attribs))
1273 	{
1274 		qpPrintf("qpTestLog_startSampleList(): Writing XML failed\n");
1275 		deMutex_unlock(log->lock);
1276 		return DE_FALSE;
1277 	}
1278 
1279 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLELIST));
1280 
1281 	deMutex_unlock(log->lock);
1282 	return DE_TRUE;
1283 }
1284 
qpTestLog_startSampleInfo(qpTestLog * log)1285 deBool qpTestLog_startSampleInfo (qpTestLog* log)
1286 {
1287 	DE_ASSERT(log);
1288 	deMutex_lock(log->lock);
1289 
1290 	if (!qpXmlWriter_startElement(log->writer, "SampleInfo", 0, DE_NULL))
1291 	{
1292 		qpPrintf("qpTestLog_startSampleInfo(): Writing XML failed\n");
1293 		deMutex_unlock(log->lock);
1294 		return DE_FALSE;
1295 	}
1296 
1297 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLEINFO));
1298 
1299 	deMutex_unlock(log->lock);
1300 	return DE_TRUE;
1301 }
1302 
qpTestLog_writeValueInfo(qpTestLog * log,const char * name,const char * description,const char * unit,qpSampleValueTag tag)1303 deBool qpTestLog_writeValueInfo (qpTestLog* log, const char* name, const char* description, const char* unit, qpSampleValueTag tag)
1304 {
1305 	const char*		tagName		= QP_LOOKUP_STRING(s_qpSampleValueTagMap, tag);
1306 	int				numAttribs	= 0;
1307 	qpXmlAttribute	attribs[4];
1308 
1309 	DE_ASSERT(log && name && description && tagName);
1310 	deMutex_lock(log->lock);
1311 
1312 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1313 
1314 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1315 	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1316 	attribs[numAttribs++] = qpSetStringAttrib("Tag", tagName);
1317 
1318 	if (unit)
1319 		attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
1320 
1321 	if (!qpXmlWriter_startElement(log->writer, "ValueInfo", numAttribs, attribs) ||
1322 		!qpXmlWriter_endElement(log->writer, "ValueInfo"))
1323 	{
1324 		qpPrintf("qpTestLog_writeValueInfo(): Writing XML failed\n");
1325 		deMutex_unlock(log->lock);
1326 		return DE_FALSE;
1327 	}
1328 
1329 	deMutex_unlock(log->lock);
1330 	return DE_TRUE;
1331 }
1332 
qpTestLog_endSampleInfo(qpTestLog * log)1333 deBool qpTestLog_endSampleInfo (qpTestLog* log)
1334 {
1335 	DE_ASSERT(log);
1336 	deMutex_lock(log->lock);
1337 
1338 	if (!qpXmlWriter_endElement(log->writer, "SampleInfo"))
1339 	{
1340 		qpPrintf("qpTestLog_endSampleInfo(): Writing XML failed\n");
1341 		deMutex_unlock(log->lock);
1342 		return DE_FALSE;
1343 	}
1344 
1345 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1346 
1347 	deMutex_unlock(log->lock);
1348 	return DE_TRUE;
1349 }
1350 
qpTestLog_startSample(qpTestLog * log)1351 deBool qpTestLog_startSample (qpTestLog* log)
1352 {
1353 	DE_ASSERT(log);
1354 	deMutex_lock(log->lock);
1355 
1356 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1357 
1358 	if (!qpXmlWriter_startElement(log->writer, "Sample", 0, DE_NULL))
1359 	{
1360 		qpPrintf("qpTestLog_startSample(): Writing XML failed\n");
1361 		deMutex_unlock(log->lock);
1362 		return DE_FALSE;
1363 	}
1364 
1365 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLE));
1366 
1367 	deMutex_unlock(log->lock);
1368 	return DE_TRUE;
1369 }
1370 
qpTestLog_writeValueFloat(qpTestLog * log,double value)1371 deBool qpTestLog_writeValueFloat (qpTestLog* log, double value)
1372 {
1373 	char tmpString[512];
1374 	doubleToString(value, tmpString, (int)sizeof(tmpString));
1375 
1376 	deMutex_lock(log->lock);
1377 
1378 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1379 
1380 	if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1381 	{
1382 		qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1383 		deMutex_unlock(log->lock);
1384 		return DE_FALSE;
1385 	}
1386 
1387 	deMutex_unlock(log->lock);
1388 	return DE_TRUE;
1389 }
1390 
qpTestLog_writeValueInteger(qpTestLog * log,deInt64 value)1391 deBool qpTestLog_writeValueInteger (qpTestLog* log, deInt64 value)
1392 {
1393 	char tmpString[64];
1394 	int64ToString(value, tmpString);
1395 
1396 	deMutex_lock(log->lock);
1397 
1398 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1399 
1400 	if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1401 	{
1402 		qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1403 		deMutex_unlock(log->lock);
1404 		return DE_FALSE;
1405 	}
1406 
1407 	deMutex_unlock(log->lock);
1408 	return DE_TRUE;
1409 }
1410 
qpTestLog_endSample(qpTestLog * log)1411 deBool qpTestLog_endSample (qpTestLog* log)
1412 {
1413 	DE_ASSERT(log);
1414 	deMutex_lock(log->lock);
1415 
1416 	if (!qpXmlWriter_endElement(log->writer, "Sample"))
1417 	{
1418 		qpPrintf("qpTestLog_endSample(): Writing XML failed\n");
1419 		deMutex_unlock(log->lock);
1420 		return DE_FALSE;
1421 	}
1422 
1423 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1424 
1425 	deMutex_unlock(log->lock);
1426 	return DE_TRUE;
1427 }
1428 
qpTestLog_endSampleList(qpTestLog * log)1429 deBool qpTestLog_endSampleList (qpTestLog* log)
1430 {
1431 	DE_ASSERT(log);
1432 	deMutex_lock(log->lock);
1433 
1434 	if (!qpXmlWriter_endElement(log->writer, "SampleList"))
1435 	{
1436 		qpPrintf("qpTestLog_endSampleList(): Writing XML failed\n");
1437 		deMutex_unlock(log->lock);
1438 		return DE_FALSE;
1439 	}
1440 
1441 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1442 
1443 	deMutex_unlock(log->lock);
1444 	return DE_TRUE;
1445 }
1446 
qpTestLog_getLogFlags(const qpTestLog * log)1447 deUint32 qpTestLog_getLogFlags (const qpTestLog* log)
1448 {
1449 	DE_ASSERT(log);
1450 	return log->flags;
1451 }
1452 
qpGetTestResultName(qpTestResult result)1453 const char* qpGetTestResultName (qpTestResult result)
1454 {
1455 	return QP_LOOKUP_STRING(s_qpTestResultMap, result);
1456 }
1457