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