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