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