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