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