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