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.4";
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,size_t bufSize)282 DE_INLINE void floatToString (float value, char* buf, size_t bufSize)
283 {
284 	deSprintf(buf, bufSize, "%f", value);
285 }
286 
doubleToString(double value,char * buf,size_t bufSize)287 DE_INLINE void doubleToString (double value, char* buf, size_t 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 	qpPrintf("Writing test log into %s\n", fileName);
344 
345 	/* Create output file. */
346 	log->outputFile = fopen(fileName, "wb");
347 	if (!log->outputFile)
348 	{
349 		qpPrintf("ERROR: Unable to open test log output file '%s'.\n", fileName);
350 		qpTestLog_destroy(log);
351 		return DE_NULL;
352 	}
353 
354 	log->flags			= flags;
355 	log->writer			= qpXmlWriter_createFileWriter(log->outputFile, 0, !(flags & QP_TEST_LOG_NO_FLUSH));
356 	log->lock			= deMutex_create(DE_NULL);
357 	log->isSessionOpen	= DE_FALSE;
358 	log->isCaseOpen		= DE_FALSE;
359 
360 	if (!log->writer)
361 	{
362 		qpPrintf("ERROR: Unable to create output XML writer to file '%s'.\n", fileName);
363 		qpTestLog_destroy(log);
364 		return DE_NULL;
365 	}
366 
367 	if (!log->lock)
368 	{
369 		qpPrintf("ERROR: Unable to create mutex.\n");
370 		qpTestLog_destroy(log);
371 		return DE_NULL;
372 	}
373 
374 	beginSession(log);
375 
376 	return log;
377 }
378 
379 /*--------------------------------------------------------------------*//*!
380  * \brief Destroy a logger instance
381  * \param a	qpTestLog instance
382  *//*--------------------------------------------------------------------*/
qpTestLog_destroy(qpTestLog * log)383 void qpTestLog_destroy (qpTestLog* log)
384 {
385 	DE_ASSERT(log);
386 
387 	if (log->isSessionOpen)
388 		endSession(log);
389 
390 	if (log->writer)
391 		qpXmlWriter_destroy(log->writer);
392 
393 	if (log->outputFile)
394 		fclose(log->outputFile);
395 
396 	if (log->lock)
397 		deMutex_destroy(log->lock);
398 
399 	deFree(log);
400 }
401 
402 /*--------------------------------------------------------------------*//*!
403  * \brief Log start of test case
404  * \param log qpTestLog instance
405  * \param testCasePath	Full test case path (as seen in Candy).
406  * \param testCaseType	Test case type
407  * \return true if ok, false otherwise
408  *//*--------------------------------------------------------------------*/
qpTestLog_startCase(qpTestLog * log,const char * testCasePath,qpTestCaseType testCaseType)409 deBool qpTestLog_startCase (qpTestLog* log, const char* testCasePath, qpTestCaseType testCaseType)
410 {
411 	const char*		typeStr				= QP_LOOKUP_STRING(s_qpTestTypeMap, testCaseType);
412 	int				numResultAttribs	= 0;
413 	qpXmlAttribute	resultAttribs[8];
414 
415 	DE_ASSERT(log && testCasePath && (testCasePath[0] != 0));
416 	deMutex_lock(log->lock);
417 
418 	DE_ASSERT(!log->isCaseOpen);
419 	DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
420 
421 	/* Flush XML and write out #beginTestCaseResult. */
422 	qpXmlWriter_flush(log->writer);
423 	fprintf(log->outputFile, "\n#beginTestCaseResult %s\n", testCasePath);
424 	if (!(log->flags & QP_TEST_LOG_NO_FLUSH))
425 		qpTestLog_flushFile(log);
426 
427 	log->isCaseOpen = DE_TRUE;
428 
429 	/* Fill in attributes. */
430 	resultAttribs[numResultAttribs++] = qpSetStringAttrib("Version", LOG_FORMAT_VERSION);
431 	resultAttribs[numResultAttribs++] = qpSetStringAttrib("CasePath", testCasePath);
432 	resultAttribs[numResultAttribs++] = qpSetStringAttrib("CaseType", typeStr);
433 
434 	if (!qpXmlWriter_startDocument(log->writer) ||
435 		!qpXmlWriter_startElement(log->writer, "TestCaseResult", numResultAttribs, resultAttribs))
436 	{
437 		qpPrintf("qpTestLog_startCase(): Writing XML failed\n");
438 		deMutex_unlock(log->lock);
439 		return DE_FALSE;
440 	}
441 
442 	deMutex_unlock(log->lock);
443 	return DE_TRUE;
444 }
445 
446 /*--------------------------------------------------------------------*//*!
447  * \brief Log end of test case
448  * \param log qpTestLog instance
449  * \param result Test result
450  * \param description Description of a problem in case of error
451  * \return true if ok, false otherwise
452  *//*--------------------------------------------------------------------*/
qpTestLog_endCase(qpTestLog * log,qpTestResult result,const char * resultDetails)453 deBool qpTestLog_endCase (qpTestLog* log, qpTestResult result, const char* resultDetails)
454 {
455 	const char*		statusStr		= QP_LOOKUP_STRING(s_qpTestResultMap, result);
456 	qpXmlAttribute	statusAttrib	= qpSetStringAttrib("StatusCode", statusStr);
457 
458 	deMutex_lock(log->lock);
459 
460 	DE_ASSERT(log->isCaseOpen);
461 	DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
462 
463 	/* <Result StatusCode="Pass">Result details</Result>
464 	 * </TestCaseResult>
465 	 */
466 	if (!qpXmlWriter_startElement(log->writer, "Result", 1, &statusAttrib) ||
467 		(resultDetails && !qpXmlWriter_writeString(log->writer, resultDetails)) ||
468 		!qpXmlWriter_endElement(log->writer, "Result") ||
469 		!qpXmlWriter_endElement(log->writer, "TestCaseResult") ||
470 		!qpXmlWriter_endDocument(log->writer))		/* Close any XML elements still open */
471 	{
472 		qpPrintf("qpTestLog_endCase(): Writing XML failed\n");
473 		deMutex_unlock(log->lock);
474 		return DE_FALSE;
475 	}
476 
477 	/* Flush XML and write #endTestCaseResult. */
478 	qpXmlWriter_flush(log->writer);
479 	fprintf(log->outputFile, "\n#endTestCaseResult\n");
480 	if (!(log->flags & QP_TEST_LOG_NO_FLUSH))
481 		qpTestLog_flushFile(log);
482 
483 	log->isCaseOpen = DE_FALSE;
484 
485 	deMutex_unlock(log->lock);
486 	return DE_TRUE;
487 }
488 
qpTestLog_startTestsCasesTime(qpTestLog * log)489 deBool qpTestLog_startTestsCasesTime (qpTestLog* log)
490 {
491 	DE_ASSERT(log);
492 	deMutex_lock(log->lock);
493 
494 	/* Flush XML and write out #beginTestCaseResult. */
495 	qpXmlWriter_flush(log->writer);
496 	fprintf(log->outputFile, "\n#beginTestsCasesTime\n");
497 
498 	log->isCaseOpen = DE_TRUE;
499 
500 	if (!qpXmlWriter_startDocument(log->writer) ||
501 		!qpXmlWriter_startElement(log->writer, "TestsCasesTime", 0, (const qpXmlAttribute*)DE_NULL))
502 	{
503 		qpPrintf("qpTestLog_startTestsCasesTime(): Writing XML failed\n");
504 		deMutex_unlock(log->lock);
505 		return DE_FALSE;
506 	}
507 
508 	deMutex_unlock(log->lock);
509 	return DE_TRUE;
510 }
511 
qpTestLog_endTestsCasesTime(qpTestLog * log)512 deBool qpTestLog_endTestsCasesTime (qpTestLog* log)
513 {
514 	DE_ASSERT(log);
515 	deMutex_lock(log->lock);
516 
517 	DE_ASSERT(log->isCaseOpen);
518 
519 	if (!qpXmlWriter_endElement(log->writer, "TestsCasesTime") ||
520 		!qpXmlWriter_endDocument(log->writer))
521 	{
522 		qpPrintf("qpTestLog_endTestsCasesTime(): Writing XML failed\n");
523 		deMutex_unlock(log->lock);
524 		return DE_FALSE;
525 	}
526 
527 	qpXmlWriter_flush(log->writer);
528 
529 	fprintf(log->outputFile, "\n#endTestsCasesTime\n");
530 
531 	log->isCaseOpen = DE_FALSE;
532 
533 	deMutex_unlock(log->lock);
534 	return DE_TRUE;
535 }
536 
537 
538 /*--------------------------------------------------------------------*//*!
539  * \brief Abrupt termination of logging.
540  * \param log		qpTestLog instance
541  * \param result	Result code, only Crash and Timeout are allowed.
542  * \return true if ok, false otherwise
543  *//*--------------------------------------------------------------------*/
qpTestLog_terminateCase(qpTestLog * log,qpTestResult result)544 deBool qpTestLog_terminateCase (qpTestLog* log, qpTestResult result)
545 {
546 	const char* resultStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
547 
548 	DE_ASSERT(log);
549 	DE_ASSERT(result == QP_TEST_RESULT_CRASH || result == QP_TEST_RESULT_TIMEOUT);
550 
551 	deMutex_lock(log->lock);
552 
553 	if (!log->isCaseOpen)
554 	{
555 		deMutex_unlock(log->lock);
556 		return DE_FALSE; /* Soft error. This is called from error handler. */
557 	}
558 
559 	/* Flush XML and write #terminateTestCaseResult. */
560 	qpXmlWriter_flush(log->writer);
561 	fprintf(log->outputFile, "\n#terminateTestCaseResult %s\n", resultStr);
562 	qpTestLog_flushFile(log);
563 
564 	log->isCaseOpen = DE_FALSE;
565 
566 #if defined(DE_DEBUG)
567 	ContainerStack_reset(&log->containerStack);
568 #endif
569 
570 	deMutex_unlock(log->lock);
571 	return DE_TRUE;
572 }
573 
qpTestLog_writeKeyValuePair(qpTestLog * log,const char * elementName,const char * name,const char * description,const char * unit,qpKeyValueTag tag,const char * text)574 static deBool qpTestLog_writeKeyValuePair (qpTestLog* log, const char* elementName, const char* name, const char* description, const char* unit, qpKeyValueTag tag, const char* text)
575 {
576 	const char*		tagString = QP_LOOKUP_STRING(s_qpTagMap, tag);
577 	qpXmlAttribute	attribs[8];
578 	int				numAttribs = 0;
579 
580 	DE_ASSERT(log && elementName && text);
581 	deMutex_lock(log->lock);
582 
583 	/* Fill in attributes. */
584 	if (name)			attribs[numAttribs++] = qpSetStringAttrib("Name", name);
585 	if (description)	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
586 	if (tagString)		attribs[numAttribs++] = qpSetStringAttrib("Tag", tagString);
587 	if (unit)			attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
588 
589 	if (!qpXmlWriter_startElement(log->writer, elementName, numAttribs, attribs) ||
590 		!qpXmlWriter_writeString(log->writer, text) ||
591 		!qpXmlWriter_endElement(log->writer, elementName))
592 	{
593 		qpPrintf("qpTestLog_writeKeyValuePair(): Writing XML failed\n");
594 		deMutex_unlock(log->lock);
595 		return DE_FALSE;
596 	}
597 
598 	deMutex_unlock(log->lock);
599 	return DE_TRUE;
600 }
601 
602 /*--------------------------------------------------------------------*//*!
603  * \brief Write key-value-pair into log
604  * \param log			qpTestLog instance
605  * \param name			Unique identifier for entry
606  * \param description	Human readable description
607  * \param tag			Optional tag
608  * \param value			Value of the key-value-pair
609  * \return true if ok, false otherwise
610  *//*--------------------------------------------------------------------*/
qpTestLog_writeText(qpTestLog * log,const char * name,const char * description,qpKeyValueTag tag,const char * text)611 deBool qpTestLog_writeText (qpTestLog* log, const char* name, const char* description, qpKeyValueTag tag, const char* text)
612 {
613 	/* <Text Name="name" Description="description" Tag="tag">text</Text> */
614 	return qpTestLog_writeKeyValuePair(log, "Text", name, description, DE_NULL, tag, text);
615 }
616 
617 /*--------------------------------------------------------------------*//*!
618  * \brief Write key-value-pair into log
619  * \param log			qpTestLog instance
620  * \param name			Unique identifier for entry
621  * \param description	Human readable description
622  * \param tag			Optional tag
623  * \param value			Value of the key-value-pair
624  * \return true if ok, false otherwise
625  *//*--------------------------------------------------------------------*/
qpTestLog_writeInteger(qpTestLog * log,const char * name,const char * description,const char * unit,qpKeyValueTag tag,deInt64 value)626 deBool qpTestLog_writeInteger (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
627 {
628 	char tmpString[64];
629 	int64ToString(value, tmpString);
630 
631 	/* <Number Name="name" Description="description" Tag="Performance">15</Number> */
632 	return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
633 }
634 
635 /*--------------------------------------------------------------------*//*!
636  * \brief Write key-value-pair into log
637  * \param log			qpTestLog instance
638  * \param name			Unique identifier for entry
639  * \param description	Human readable description
640  * \param tag			Optional tag
641  * \param value			Value of the key-value-pair
642  * \return true if ok, false otherwise
643  *//*--------------------------------------------------------------------*/
qpTestLog_writeFloat(qpTestLog * log,const char * name,const char * description,const char * unit,qpKeyValueTag tag,float value)644 deBool qpTestLog_writeFloat (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
645 {
646 	char tmpString[64];
647 	floatToString(value, tmpString, sizeof(tmpString));
648 
649 	/* <Number Name="name" Description="description" Tag="Performance">15</Number> */
650 	return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
651 }
652 
653 typedef struct Buffer_s
654 {
655 	size_t		capacity;
656 	size_t		size;
657 	deUint8*	data;
658 } Buffer;
659 
Buffer_init(Buffer * buffer)660 void Buffer_init (Buffer* buffer)
661 {
662 	buffer->capacity	= 0;
663 	buffer->size		= 0;
664 	buffer->data		= DE_NULL;
665 }
666 
Buffer_deinit(Buffer * buffer)667 void Buffer_deinit (Buffer* buffer)
668 {
669 	deFree(buffer->data);
670 	Buffer_init(buffer);
671 }
672 
Buffer_resize(Buffer * buffer,size_t newSize)673 deBool Buffer_resize (Buffer* buffer, size_t newSize)
674 {
675 	/* Grow buffer if necessary. */
676 	if (newSize > buffer->capacity)
677 	{
678 		size_t		newCapacity	= (size_t)deAlign32(deMax32(2*(int)buffer->capacity, (int)newSize), 512);
679 		deUint8*	newData		= (deUint8*)deMalloc(newCapacity);
680 		if (!newData)
681 			return DE_FALSE;
682 
683 		memcpy(newData, buffer->data, buffer->size);
684 		deFree(buffer->data);
685 		buffer->data		= newData;
686 		buffer->capacity	= newCapacity;
687 	}
688 
689 	buffer->size = newSize;
690 	return DE_TRUE;
691 }
692 
Buffer_append(Buffer * buffer,const deUint8 * data,size_t numBytes)693 deBool Buffer_append (Buffer* buffer, const deUint8* data, size_t numBytes)
694 {
695 	size_t offset = buffer->size;
696 
697 	if (!Buffer_resize(buffer, buffer->size + numBytes))
698 		return DE_FALSE;
699 
700 	/* Append bytes. */
701 	memcpy(&buffer->data[offset], data, numBytes);
702 	return DE_TRUE;
703 }
704 
705 #if defined(QP_SUPPORT_PNG)
pngWriteData(png_structp png,png_bytep dataPtr,png_size_t numBytes)706 void pngWriteData (png_structp png, png_bytep dataPtr, png_size_t numBytes)
707 {
708 	Buffer* buffer = (Buffer*)png_get_io_ptr(png);
709 	if (!Buffer_append(buffer, (const deUint8*)dataPtr, numBytes))
710 		png_error(png, "unable to resize PNG write buffer!");
711 }
712 
pngFlushData(png_structp png)713 void pngFlushData (png_structp png)
714 {
715 	DE_UNREF(png);
716 	/* nada */
717 }
718 
writeCompressedPNG(png_structp png,png_infop info,png_byte ** rowPointers,int width,int height,int colorFormat)719 static deBool writeCompressedPNG (png_structp png, png_infop info, png_byte** rowPointers, int width, int height, int colorFormat)
720 {
721 	if (setjmp(png_jmpbuf(png)) == 0)
722 	{
723 		/* Write data. */
724 		png_set_IHDR(png, info, (png_uint_32)width, (png_uint_32)height,
725 			8,
726 			colorFormat,
727 			PNG_INTERLACE_NONE,
728 			PNG_COMPRESSION_TYPE_BASE,
729 			PNG_FILTER_TYPE_BASE);
730 		png_write_info(png, info);
731 		png_write_image(png, rowPointers);
732 		png_write_end(png, NULL);
733 
734 		return DE_TRUE;
735 	}
736 	else
737 		return DE_FALSE;
738 }
739 
compressImagePNG(Buffer * buffer,qpImageFormat imageFormat,int width,int height,int rowStride,const void * data)740 static deBool compressImagePNG (Buffer* buffer, qpImageFormat imageFormat, int width, int height, int rowStride, const void* data)
741 {
742 	deBool			compressOk		= DE_FALSE;
743 	png_structp		png				= DE_NULL;
744 	png_infop		info			= DE_NULL;
745 	png_byte**		rowPointers		= DE_NULL;
746 	deBool			hasAlpha		= imageFormat == QP_IMAGE_FORMAT_RGBA8888;
747 	int				ndx;
748 
749 	/* Handle format. */
750 	DE_ASSERT(imageFormat == QP_IMAGE_FORMAT_RGB888 || imageFormat == QP_IMAGE_FORMAT_RGBA8888);
751 
752 	/* Allocate & set row pointers. */
753 	rowPointers = (png_byte**)deMalloc((size_t)height * sizeof(png_byte*));
754 	if (!rowPointers)
755 		return DE_FALSE;
756 
757 	for (ndx = 0; ndx < height; ndx++)
758 		rowPointers[ndx] = (png_byte*)((const deUint8*)data + ndx*rowStride);
759 
760 	/* Initialize PNG compressor. */
761 	png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
762 	info = png ? png_create_info_struct(png) : DE_NULL;
763 	if (png && info)
764 	{
765 		/* Set our own write function. */
766 		png_set_write_fn(png, buffer, pngWriteData, pngFlushData);
767 
768 		compressOk = writeCompressedPNG(png, info, rowPointers, width, height,
769 										hasAlpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
770 	}
771 
772 	/* Cleanup & return. */
773 	if (png && info)
774 	{
775 		png_destroy_info_struct(png, &info);
776 		png_destroy_write_struct(&png, DE_NULL);
777 	}
778 	else if (png)
779 		png_destroy_write_struct(&png, &info);
780 
781 	deFree(rowPointers);
782 	return compressOk;
783 }
784 #endif /* QP_SUPPORT_PNG */
785 
786 /*--------------------------------------------------------------------*//*!
787  * \brief Start image set
788  * \param log			qpTestLog instance
789  * \param name			Unique identifier for the set
790  * \param description	Human readable description
791  * \return true if ok, false otherwise
792  *//*--------------------------------------------------------------------*/
qpTestLog_startImageSet(qpTestLog * log,const char * name,const char * description)793 deBool qpTestLog_startImageSet (qpTestLog* log, const char* name, const char* description)
794 {
795 	qpXmlAttribute	attribs[4];
796 	int				numAttribs = 0;
797 
798 	DE_ASSERT(log && name);
799 	deMutex_lock(log->lock);
800 
801 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
802 	if (description)
803 		attribs[numAttribs++] = qpSetStringAttrib("Description", description);
804 
805 	/* <ImageSet Name="<name>"> */
806 	if (!qpXmlWriter_startElement(log->writer, "ImageSet", numAttribs, attribs))
807 	{
808 		qpPrintf("qpTestLog_startImageSet(): Writing XML failed\n");
809 		deMutex_unlock(log->lock);
810 		return DE_FALSE;
811 	}
812 
813 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_IMAGESET));
814 
815 	deMutex_unlock(log->lock);
816 	return DE_TRUE;
817 }
818 
819 /*--------------------------------------------------------------------*//*!
820  * \brief End image set
821  * \param log			qpTestLog instance
822  * \return true if ok, false otherwise
823  *//*--------------------------------------------------------------------*/
qpTestLog_endImageSet(qpTestLog * log)824 deBool qpTestLog_endImageSet (qpTestLog* log)
825 {
826 	DE_ASSERT(log);
827 	deMutex_lock(log->lock);
828 
829 	/* <ImageSet Name="<name>"> */
830 	if (!qpXmlWriter_endElement(log->writer, "ImageSet"))
831 	{
832 		qpPrintf("qpTestLog_endImageSet(): Writing XML failed\n");
833 		deMutex_unlock(log->lock);
834 		return DE_FALSE;
835 	}
836 
837 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_IMAGESET);
838 
839 	deMutex_unlock(log->lock);
840 	return DE_TRUE;
841 }
842 
843 /*--------------------------------------------------------------------*//*!
844  * \brief Write base64 encoded raw image data into log
845  * \param log				qpTestLog instance
846  * \param name				Unique name (matching names can be compared across BatchResults).
847  * \param description		Textual description (shown in Candy).
848  * \param compressionMode	Compression mode
849  * \param imageFormat		Color format
850  * \param width				Width in pixels
851  * \param height			Height in pixels
852  * \param stride			Data stride (offset between rows)
853  * \param data				Pointer to pixel data
854  * \return 0 if OK, otherwise <0
855  *//*--------------------------------------------------------------------*/
qpTestLog_writeImage(qpTestLog * log,const char * name,const char * description,qpImageCompressionMode compressionMode,qpImageFormat imageFormat,int width,int height,int stride,const void * data)856 deBool qpTestLog_writeImage	(
857 	qpTestLog*				log,
858 	const char*				name,
859 	const char*				description,
860 	qpImageCompressionMode	compressionMode,
861 	qpImageFormat			imageFormat,
862 	int						width,
863 	int						height,
864 	int						stride,
865 	const void*				data)
866 {
867 	char			widthStr[32];
868 	char			heightStr[32];
869 	qpXmlAttribute	attribs[8];
870 	int				numAttribs			= 0;
871 	Buffer			compressedBuffer;
872 	const void*		writeDataPtr		= DE_NULL;
873 	size_t			writeDataBytes		= ~(size_t)0;
874 
875 	DE_ASSERT(log && name);
876 	DE_ASSERT(deInRange32(width, 1, 32768));
877 	DE_ASSERT(deInRange32(height, 1, 32768));
878 	DE_ASSERT(data);
879 
880 	if (log->flags & QP_TEST_LOG_EXCLUDE_IMAGES)
881 		return DE_TRUE; /* Image not logged. */
882 
883 	Buffer_init(&compressedBuffer);
884 
885 	/* BEST compression mode defaults to PNG. */
886 	if (compressionMode == QP_IMAGE_COMPRESSION_MODE_BEST)
887 	{
888 #if defined(QP_SUPPORT_PNG)
889 		compressionMode = QP_IMAGE_COMPRESSION_MODE_PNG;
890 #else
891 		compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
892 #endif
893 	}
894 
895 #if defined(QP_SUPPORT_PNG)
896 	/* Try storing with PNG compression. */
897 	if (compressionMode == QP_IMAGE_COMPRESSION_MODE_PNG)
898 	{
899 		deBool compressOk = compressImagePNG(&compressedBuffer, imageFormat, width, height, stride, data);
900 		if (compressOk)
901 		{
902 			writeDataPtr	= compressedBuffer.data;
903 			writeDataBytes	= compressedBuffer.size;
904 		}
905 		else
906 		{
907 			/* Fall-back to default compression. */
908 			qpPrintf("WARNING: PNG compression failed -- storing image uncompressed.\n");
909 			compressionMode	= QP_IMAGE_COMPRESSION_MODE_NONE;
910 		}
911 	}
912 #endif
913 
914 	/* Handle image compression. */
915 	switch (compressionMode)
916 	{
917 		case QP_IMAGE_COMPRESSION_MODE_NONE:
918 		{
919 			int pixelSize		= imageFormat == QP_IMAGE_FORMAT_RGB888 ? 3 : 4;
920 			int packedStride	= pixelSize*width;
921 
922 			if (packedStride == stride)
923 				writeDataPtr = data;
924 			else
925 			{
926 				/* Need to re-pack pixels. */
927 				if (Buffer_resize(&compressedBuffer, (size_t)(packedStride*height)))
928 				{
929 					int row;
930 					for (row = 0; row < height; row++)
931 						memcpy(&compressedBuffer.data[packedStride*row], &((const deUint8*)data)[row*stride], (size_t)(pixelSize*width));
932 				}
933 				else
934 				{
935 					qpPrintf("ERROR: Failed to pack pixels for writing.\n");
936 					Buffer_deinit(&compressedBuffer);
937 					return DE_FALSE;
938 				}
939 			}
940 
941 			writeDataBytes = (size_t)(packedStride*height);
942 			break;
943 		}
944 
945 #if defined(QP_SUPPORT_PNG)
946 		case QP_IMAGE_COMPRESSION_MODE_PNG:
947 			DE_ASSERT(writeDataPtr); /* Already handled. */
948 			break;
949 #endif
950 
951 		default:
952 			qpPrintf("qpTestLog_writeImage(): Unknown compression mode: %s\n", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
953 			Buffer_deinit(&compressedBuffer);
954 			return DE_FALSE;
955 	}
956 
957 	/* Fill in attributes. */
958 	int32ToString(width, widthStr);
959 	int32ToString(height, heightStr);
960 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
961 	attribs[numAttribs++] = qpSetStringAttrib("Width", widthStr);
962 	attribs[numAttribs++] = qpSetStringAttrib("Height", heightStr);
963 	attribs[numAttribs++] = qpSetStringAttrib("Format", QP_LOOKUP_STRING(s_qpImageFormatMap, imageFormat));
964 	attribs[numAttribs++] = qpSetStringAttrib("CompressionMode", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
965 	if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
966 
967 	/* \note Log lock is acquired after compression! */
968 	deMutex_lock(log->lock);
969 
970 	/* <Image ID="result" Name="Foobar" Width="640" Height="480" Format="RGB888" CompressionMode="None">base64 data</Image> */
971 	if (!qpXmlWriter_startElement(log->writer, "Image", numAttribs, attribs) ||
972 		!qpXmlWriter_writeBase64(log->writer, (const deUint8*)writeDataPtr, writeDataBytes) ||
973 		!qpXmlWriter_endElement(log->writer, "Image"))
974 	{
975 		qpPrintf("qpTestLog_writeImage(): Writing XML failed\n");
976 		deMutex_unlock(log->lock);
977 		Buffer_deinit(&compressedBuffer);
978 		return DE_FALSE;
979 	}
980 
981 	deMutex_unlock(log->lock);
982 
983 	/* Free compressed data if allocated. */
984 	Buffer_deinit(&compressedBuffer);
985 
986 	return DE_TRUE;
987 }
988 
989 /*--------------------------------------------------------------------*//*!
990  * \brief Write a OpenGL ES shader program into the log.
991  * \param linkOk			Shader program link result, false on failure
992  * \param linkInfoLog		Implementation provided linkage log
993  *//*--------------------------------------------------------------------*/
qpTestLog_startShaderProgram(qpTestLog * log,deBool linkOk,const char * linkInfoLog)994 deBool qpTestLog_startShaderProgram (qpTestLog* log, deBool linkOk, const char* linkInfoLog)
995 {
996 	qpXmlAttribute	programAttribs[4];
997 	int				numProgramAttribs = 0;
998 
999 	DE_ASSERT(log);
1000 	deMutex_lock(log->lock);
1001 
1002 	programAttribs[numProgramAttribs++] = qpSetStringAttrib("LinkStatus", linkOk ? "OK" : "Fail");
1003 
1004 	if (!qpXmlWriter_startElement(log->writer, "ShaderProgram", numProgramAttribs, programAttribs) ||
1005 		!qpXmlWriter_writeStringElement(log->writer, "InfoLog", linkInfoLog))
1006 	{
1007 		qpPrintf("qpTestLog_startShaderProgram(): Writing XML failed\n");
1008 		deMutex_unlock(log->lock);
1009 		return DE_FALSE;
1010 	}
1011 
1012 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SHADERPROGRAM));
1013 
1014 	deMutex_unlock(log->lock);
1015 	return DE_TRUE;
1016 }
1017 
1018 /*--------------------------------------------------------------------*//*!
1019  * \brief End shader program
1020  * \param log			qpTestLog instance
1021  * \return true if ok, false otherwise
1022  *//*--------------------------------------------------------------------*/
qpTestLog_endShaderProgram(qpTestLog * log)1023 deBool qpTestLog_endShaderProgram (qpTestLog* log)
1024 {
1025 	DE_ASSERT(log);
1026 	deMutex_lock(log->lock);
1027 
1028 	/* </ShaderProgram> */
1029 	if (!qpXmlWriter_endElement(log->writer, "ShaderProgram"))
1030 	{
1031 		qpPrintf("qpTestLog_endShaderProgram(): Writing XML failed\n");
1032 		deMutex_unlock(log->lock);
1033 		return DE_FALSE;
1034 	}
1035 
1036 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1037 
1038 	deMutex_unlock(log->lock);
1039 	return DE_TRUE;
1040 }
1041 
1042 /*--------------------------------------------------------------------*//*!
1043  * \brief Write a OpenGL ES shader into the log.
1044  * \param type				Shader type
1045  * \param source			Shader source
1046  * \param compileOk			Shader compilation result, false on failure
1047  * \param infoLog			Implementation provided shader compilation log
1048  *//*--------------------------------------------------------------------*/
qpTestLog_writeShader(qpTestLog * log,qpShaderType type,const char * source,deBool compileOk,const char * infoLog)1049 deBool qpTestLog_writeShader (qpTestLog* log, qpShaderType type, const char* source, deBool compileOk, const char* infoLog)
1050 {
1051 	const char*		tagName				= QP_LOOKUP_STRING(s_qpShaderTypeMap, type);
1052 	const char*		sourceStr			= ((log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) == 0 || !compileOk) ? source : "";
1053 	int				numShaderAttribs	= 0;
1054 	qpXmlAttribute	shaderAttribs[4];
1055 
1056 	deMutex_lock(log->lock);
1057 
1058 	DE_ASSERT(source);
1059 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1060 
1061 	shaderAttribs[numShaderAttribs++]	= qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1062 
1063 	if (!qpXmlWriter_startElement(log->writer, tagName, numShaderAttribs, shaderAttribs) ||
1064 		!qpXmlWriter_writeStringElement(log->writer, "ShaderSource", sourceStr) ||
1065 		!qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1066 		!qpXmlWriter_endElement(log->writer, tagName))
1067 	{
1068 		qpPrintf("qpTestLog_writeShader(): Writing XML failed\n");
1069 		deMutex_unlock(log->lock);
1070 		return DE_FALSE;
1071 	}
1072 
1073 	deMutex_unlock(log->lock);
1074 	return DE_TRUE;
1075 }
1076 
1077 /*--------------------------------------------------------------------*//*!
1078  * \brief Start writing a set of EGL configurations into the log.
1079  *//*--------------------------------------------------------------------*/
qpTestLog_startEglConfigSet(qpTestLog * log,const char * name,const char * description)1080 deBool qpTestLog_startEglConfigSet (qpTestLog* log, const char* name, const char* description)
1081 {
1082 	qpXmlAttribute	attribs[4];
1083 	int				numAttribs = 0;
1084 
1085 	DE_ASSERT(log && name);
1086 	deMutex_lock(log->lock);
1087 
1088 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1089 	if (description)
1090 		attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1091 
1092 	/* <EglConfigSet Name="<name>"> */
1093 	if (!qpXmlWriter_startElement(log->writer, "EglConfigSet", numAttribs, attribs))
1094 	{
1095 		qpPrintf("qpTestLog_startEglImageSet(): Writing XML failed\n");
1096 		deMutex_unlock(log->lock);
1097 		return DE_FALSE;
1098 	}
1099 
1100 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_EGLCONFIGSET));
1101 
1102 	deMutex_unlock(log->lock);
1103 	return DE_TRUE;
1104 }
1105 
1106 /*--------------------------------------------------------------------*//*!
1107  * \brief End an EGL config set
1108  *//*--------------------------------------------------------------------*/
qpTestLog_endEglConfigSet(qpTestLog * log)1109 deBool qpTestLog_endEglConfigSet (qpTestLog* log)
1110 {
1111 	DE_ASSERT(log);
1112 	deMutex_lock(log->lock);
1113 
1114 	/* <EglConfigSet Name="<name>"> */
1115 	if (!qpXmlWriter_endElement(log->writer, "EglConfigSet"))
1116 	{
1117 		qpPrintf("qpTestLog_endEglImageSet(): Writing XML failed\n");
1118 		deMutex_unlock(log->lock);
1119 		return DE_FALSE;
1120 	}
1121 
1122 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_EGLCONFIGSET);
1123 
1124 	deMutex_unlock(log->lock);
1125 	return DE_TRUE;
1126 }
1127 
1128 /*--------------------------------------------------------------------*//*!
1129  * \brief Write an EGL config inside an EGL config set
1130  * \see   qpElgConfigInfo for details
1131  *//*--------------------------------------------------------------------*/
qpTestLog_writeEglConfig(qpTestLog * log,const qpEglConfigInfo * config)1132 deBool qpTestLog_writeEglConfig (qpTestLog* log, const qpEglConfigInfo* config)
1133 {
1134 	qpXmlAttribute	attribs[64];
1135 	int				numAttribs = 0;
1136 
1137 	DE_ASSERT(log && config);
1138 	deMutex_lock(log->lock);
1139 
1140 	attribs[numAttribs++] = qpSetIntAttrib		("BufferSize", config->bufferSize);
1141 	attribs[numAttribs++] = qpSetIntAttrib		("RedSize", config->redSize);
1142 	attribs[numAttribs++] = qpSetIntAttrib		("GreenSize", config->greenSize);
1143 	attribs[numAttribs++] = qpSetIntAttrib		("BlueSize", config->blueSize);
1144 	attribs[numAttribs++] = qpSetIntAttrib		("LuminanceSize", config->luminanceSize);
1145 	attribs[numAttribs++] = qpSetIntAttrib		("AlphaSize", config->alphaSize);
1146 	attribs[numAttribs++] = qpSetIntAttrib		("AlphaMaskSize", config->alphaMaskSize);
1147 	attribs[numAttribs++] = qpSetBoolAttrib		("BindToTextureRGB", config->bindToTextureRGB);
1148 	attribs[numAttribs++] = qpSetBoolAttrib		("BindToTextureRGBA", config->bindToTextureRGBA);
1149 	attribs[numAttribs++] = qpSetStringAttrib	("ColorBufferType", config->colorBufferType);
1150 	attribs[numAttribs++] = qpSetStringAttrib	("ConfigCaveat", config->configCaveat);
1151 	attribs[numAttribs++] = qpSetIntAttrib		("ConfigID", config->configID);
1152 	attribs[numAttribs++] = qpSetStringAttrib	("Conformant", config->conformant);
1153 	attribs[numAttribs++] = qpSetIntAttrib		("DepthSize", config->depthSize);
1154 	attribs[numAttribs++] = qpSetIntAttrib		("Level", config->level);
1155 	attribs[numAttribs++] = qpSetIntAttrib		("MaxPBufferWidth", config->maxPBufferWidth);
1156 	attribs[numAttribs++] = qpSetIntAttrib		("MaxPBufferHeight", config->maxPBufferHeight);
1157 	attribs[numAttribs++] = qpSetIntAttrib		("MaxPBufferPixels", config->maxPBufferPixels);
1158 	attribs[numAttribs++] = qpSetIntAttrib		("MaxSwapInterval", config->maxSwapInterval);
1159 	attribs[numAttribs++] = qpSetIntAttrib		("MinSwapInterval", config->minSwapInterval);
1160 	attribs[numAttribs++] = qpSetBoolAttrib		("NativeRenderable", config->nativeRenderable);
1161 	attribs[numAttribs++] = qpSetStringAttrib	("RenderableType", config->renderableType);
1162 	attribs[numAttribs++] = qpSetIntAttrib		("SampleBuffers", config->sampleBuffers);
1163 	attribs[numAttribs++] = qpSetIntAttrib		("Samples", config->samples);
1164 	attribs[numAttribs++] = qpSetIntAttrib		("StencilSize", config->stencilSize);
1165 	attribs[numAttribs++] = qpSetStringAttrib	("SurfaceTypes", config->surfaceTypes);
1166 	attribs[numAttribs++] = qpSetStringAttrib	("TransparentType", config->transparentType);
1167 	attribs[numAttribs++] = qpSetIntAttrib		("TransparentRedValue", config->transparentRedValue);
1168 	attribs[numAttribs++] = qpSetIntAttrib		("TransparentGreenValue", config->transparentGreenValue);
1169 	attribs[numAttribs++] = qpSetIntAttrib		("TransparentBlueValue", config->transparentBlueValue);
1170 	DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
1171 
1172 	if (!qpXmlWriter_startElement(log->writer, "EglConfig", numAttribs, attribs) ||
1173 		!qpXmlWriter_endElement(log->writer, "EglConfig"))
1174 	{
1175 		qpPrintf("qpTestLog_writeEglConfig(): Writing XML failed\n");
1176 		deMutex_unlock(log->lock);
1177 		return DE_FALSE;
1178 	}
1179 
1180 	deMutex_unlock(log->lock);
1181 	return DE_TRUE;
1182 }
1183 
1184 /*--------------------------------------------------------------------*//*!
1185  * \brief Start section in log.
1186  * \param log			qpTestLog instance
1187  * \param name			Section name
1188  * \param description	Human readable description
1189  * \return true if ok, false otherwise
1190  *//*--------------------------------------------------------------------*/
qpTestLog_startSection(qpTestLog * log,const char * name,const char * description)1191 deBool qpTestLog_startSection (qpTestLog* log, const char* name, const char* description)
1192 {
1193 	qpXmlAttribute	attribs[2];
1194 	int				numAttribs = 0;
1195 
1196 	DE_ASSERT(log && name);
1197 	deMutex_lock(log->lock);
1198 
1199 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1200 	if (description)
1201 		attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1202 
1203 	/* <Section Name="<name>" Description="<description>"> */
1204 	if (!qpXmlWriter_startElement(log->writer, "Section", numAttribs, attribs))
1205 	{
1206 		qpPrintf("qpTestLog_startSection(): Writing XML failed\n");
1207 		deMutex_unlock(log->lock);
1208 		return DE_FALSE;
1209 	}
1210 
1211 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SECTION));
1212 
1213 	deMutex_unlock(log->lock);
1214 	return DE_TRUE;
1215 }
1216 
1217 /*--------------------------------------------------------------------*//*!
1218  * \brief End section in log.
1219  * \param log			qpTestLog instance
1220  * \return true if ok, false otherwise
1221  *//*--------------------------------------------------------------------*/
qpTestLog_endSection(qpTestLog * log)1222 deBool qpTestLog_endSection (qpTestLog* log)
1223 {
1224 	DE_ASSERT(log);
1225 	deMutex_lock(log->lock);
1226 
1227 	/* </Section> */
1228 	if (!qpXmlWriter_endElement(log->writer, "Section"))
1229 	{
1230 		qpPrintf("qpTestLog_endSection(): Writing XML failed\n");
1231 		deMutex_unlock(log->lock);
1232 		return DE_FALSE;
1233 	}
1234 
1235 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SECTION);
1236 
1237 	deMutex_unlock(log->lock);
1238 	return DE_TRUE;
1239 }
1240 
1241 /*--------------------------------------------------------------------*//*!
1242  * \brief Write OpenCL compute kernel source into the log.
1243  *//*--------------------------------------------------------------------*/
qpTestLog_writeKernelSource(qpTestLog * log,const char * source)1244 deBool qpTestLog_writeKernelSource (qpTestLog* log, const char* source)
1245 {
1246 	const char*		sourceStr	= (log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) != 0 ? "" : source;
1247 
1248 	DE_ASSERT(log);
1249 	deMutex_lock(log->lock);
1250 
1251 	if (!qpXmlWriter_writeStringElement(log->writer, "KernelSource", sourceStr))
1252 	{
1253 		qpPrintf("qpTestLog_writeKernelSource(): Writing XML failed\n");
1254 		deMutex_unlock(log->lock);
1255 		return DE_FALSE;
1256 	}
1257 
1258 	deMutex_unlock(log->lock);
1259 	return DE_TRUE;
1260 }
1261 
1262 /*--------------------------------------------------------------------*//*!
1263  * \brief Write a SPIR-V module assembly source into the log.
1264  *//*--------------------------------------------------------------------*/
qpTestLog_writeSpirVAssemblySource(qpTestLog * log,const char * source)1265 deBool qpTestLog_writeSpirVAssemblySource (qpTestLog* log, const char* source)
1266 {
1267 	const char* const	sourceStr	= (log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) != 0 ? "" : source;
1268 
1269 	deMutex_lock(log->lock);
1270 
1271 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1272 
1273 	if (!qpXmlWriter_writeStringElement(log->writer, "SpirVAssemblySource", sourceStr))
1274 	{
1275 		qpPrintf("qpTestLog_writeSpirVAssemblySource(): Writing XML failed\n");
1276 		deMutex_unlock(log->lock);
1277 		return DE_FALSE;
1278 	}
1279 
1280 	deMutex_unlock(log->lock);
1281 	return DE_TRUE;
1282 }
1283 
1284 /*--------------------------------------------------------------------*//*!
1285  * \brief Write OpenCL kernel compilation results into the log
1286  *//*--------------------------------------------------------------------*/
qpTestLog_writeCompileInfo(qpTestLog * log,const char * name,const char * description,deBool compileOk,const char * infoLog)1287 deBool qpTestLog_writeCompileInfo (qpTestLog* log, const char* name, const char* description, deBool compileOk, const char* infoLog)
1288 {
1289 	int				numAttribs = 0;
1290 	qpXmlAttribute	attribs[3];
1291 
1292 	DE_ASSERT(log && name && description && infoLog);
1293 	deMutex_lock(log->lock);
1294 
1295 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1296 	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1297 	attribs[numAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1298 
1299 	if (!qpXmlWriter_startElement(log->writer, "CompileInfo", numAttribs, attribs) ||
1300 		!qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1301 		!qpXmlWriter_endElement(log->writer, "CompileInfo"))
1302 	{
1303 		qpPrintf("qpTestLog_writeCompileInfo(): Writing XML failed\n");
1304 		deMutex_unlock(log->lock);
1305 		return DE_FALSE;
1306 	}
1307 
1308 	deMutex_unlock(log->lock);
1309 	return DE_TRUE;
1310 }
1311 
qpTestLog_startSampleList(qpTestLog * log,const char * name,const char * description)1312 deBool qpTestLog_startSampleList (qpTestLog* log, const char* name, const char* description)
1313 {
1314 	int				numAttribs = 0;
1315 	qpXmlAttribute	attribs[2];
1316 
1317 	DE_ASSERT(log && name && description);
1318 	deMutex_lock(log->lock);
1319 
1320 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1321 	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1322 
1323 	if (!qpXmlWriter_startElement(log->writer, "SampleList", numAttribs, attribs))
1324 	{
1325 		qpPrintf("qpTestLog_startSampleList(): Writing XML failed\n");
1326 		deMutex_unlock(log->lock);
1327 		return DE_FALSE;
1328 	}
1329 
1330 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLELIST));
1331 
1332 	deMutex_unlock(log->lock);
1333 	return DE_TRUE;
1334 }
1335 
qpTestLog_startSampleInfo(qpTestLog * log)1336 deBool qpTestLog_startSampleInfo (qpTestLog* log)
1337 {
1338 	DE_ASSERT(log);
1339 	deMutex_lock(log->lock);
1340 
1341 	if (!qpXmlWriter_startElement(log->writer, "SampleInfo", 0, DE_NULL))
1342 	{
1343 		qpPrintf("qpTestLog_startSampleInfo(): Writing XML failed\n");
1344 		deMutex_unlock(log->lock);
1345 		return DE_FALSE;
1346 	}
1347 
1348 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLEINFO));
1349 
1350 	deMutex_unlock(log->lock);
1351 	return DE_TRUE;
1352 }
1353 
qpTestLog_writeValueInfo(qpTestLog * log,const char * name,const char * description,const char * unit,qpSampleValueTag tag)1354 deBool qpTestLog_writeValueInfo (qpTestLog* log, const char* name, const char* description, const char* unit, qpSampleValueTag tag)
1355 {
1356 	const char*		tagName		= QP_LOOKUP_STRING(s_qpSampleValueTagMap, tag);
1357 	int				numAttribs	= 0;
1358 	qpXmlAttribute	attribs[4];
1359 
1360 	DE_ASSERT(log && name && description && tagName);
1361 	deMutex_lock(log->lock);
1362 
1363 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1364 
1365 	attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1366 	attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1367 	attribs[numAttribs++] = qpSetStringAttrib("Tag", tagName);
1368 
1369 	if (unit)
1370 		attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
1371 
1372 	if (!qpXmlWriter_startElement(log->writer, "ValueInfo", numAttribs, attribs) ||
1373 		!qpXmlWriter_endElement(log->writer, "ValueInfo"))
1374 	{
1375 		qpPrintf("qpTestLog_writeValueInfo(): Writing XML failed\n");
1376 		deMutex_unlock(log->lock);
1377 		return DE_FALSE;
1378 	}
1379 
1380 	deMutex_unlock(log->lock);
1381 	return DE_TRUE;
1382 }
1383 
qpTestLog_endSampleInfo(qpTestLog * log)1384 deBool qpTestLog_endSampleInfo (qpTestLog* log)
1385 {
1386 	DE_ASSERT(log);
1387 	deMutex_lock(log->lock);
1388 
1389 	if (!qpXmlWriter_endElement(log->writer, "SampleInfo"))
1390 	{
1391 		qpPrintf("qpTestLog_endSampleInfo(): Writing XML failed\n");
1392 		deMutex_unlock(log->lock);
1393 		return DE_FALSE;
1394 	}
1395 
1396 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1397 
1398 	deMutex_unlock(log->lock);
1399 	return DE_TRUE;
1400 }
1401 
qpTestLog_startSample(qpTestLog * log)1402 deBool qpTestLog_startSample (qpTestLog* log)
1403 {
1404 	DE_ASSERT(log);
1405 	deMutex_lock(log->lock);
1406 
1407 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1408 
1409 	if (!qpXmlWriter_startElement(log->writer, "Sample", 0, DE_NULL))
1410 	{
1411 		qpPrintf("qpTestLog_startSample(): Writing XML failed\n");
1412 		deMutex_unlock(log->lock);
1413 		return DE_FALSE;
1414 	}
1415 
1416 	DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLE));
1417 
1418 	deMutex_unlock(log->lock);
1419 	return DE_TRUE;
1420 }
1421 
qpTestLog_writeValueFloat(qpTestLog * log,double value)1422 deBool qpTestLog_writeValueFloat (qpTestLog* log, double value)
1423 {
1424 	char tmpString[512];
1425 	doubleToString(value, tmpString, (int)sizeof(tmpString));
1426 
1427 	deMutex_lock(log->lock);
1428 
1429 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1430 
1431 	if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1432 	{
1433 		qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1434 		deMutex_unlock(log->lock);
1435 		return DE_FALSE;
1436 	}
1437 
1438 	deMutex_unlock(log->lock);
1439 	return DE_TRUE;
1440 }
1441 
qpTestLog_writeValueInteger(qpTestLog * log,deInt64 value)1442 deBool qpTestLog_writeValueInteger (qpTestLog* log, deInt64 value)
1443 {
1444 	char tmpString[64];
1445 	int64ToString(value, tmpString);
1446 
1447 	deMutex_lock(log->lock);
1448 
1449 	DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1450 
1451 	if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1452 	{
1453 		qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1454 		deMutex_unlock(log->lock);
1455 		return DE_FALSE;
1456 	}
1457 
1458 	deMutex_unlock(log->lock);
1459 	return DE_TRUE;
1460 }
1461 
qpTestLog_endSample(qpTestLog * log)1462 deBool qpTestLog_endSample (qpTestLog* log)
1463 {
1464 	DE_ASSERT(log);
1465 	deMutex_lock(log->lock);
1466 
1467 	if (!qpXmlWriter_endElement(log->writer, "Sample"))
1468 	{
1469 		qpPrintf("qpTestLog_endSample(): Writing XML failed\n");
1470 		deMutex_unlock(log->lock);
1471 		return DE_FALSE;
1472 	}
1473 
1474 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1475 
1476 	deMutex_unlock(log->lock);
1477 	return DE_TRUE;
1478 }
1479 
qpTestLog_endSampleList(qpTestLog * log)1480 deBool qpTestLog_endSampleList (qpTestLog* log)
1481 {
1482 	DE_ASSERT(log);
1483 	deMutex_lock(log->lock);
1484 
1485 	if (!qpXmlWriter_endElement(log->writer, "SampleList"))
1486 	{
1487 		qpPrintf("qpTestLog_endSampleList(): Writing XML failed\n");
1488 		deMutex_unlock(log->lock);
1489 		return DE_FALSE;
1490 	}
1491 
1492 	DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1493 
1494 	deMutex_unlock(log->lock);
1495 	return DE_TRUE;
1496 }
1497 
qpTestLog_getLogFlags(const qpTestLog * log)1498 deUint32 qpTestLog_getLogFlags (const qpTestLog* log)
1499 {
1500 	DE_ASSERT(log);
1501 	return log->flags;
1502 }
1503 
qpGetTestResultName(qpTestResult result)1504 const char* qpGetTestResultName (qpTestResult result)
1505 {
1506 	return QP_LOOKUP_STRING(s_qpTestResultMap, result);
1507 }
1508