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