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