Merge vk-gl-cts/vulkan-cts-1.2.6 into vk-gl-cts/vulkan-cts-1.2.7
[platform/upstream/VK-GL-CTS.git] / executor / xeTestLogWriter.cpp
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Test Executor
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 log writer.
22  *//*--------------------------------------------------------------------*/
23
24 #include "xeTestLogWriter.hpp"
25 #include "xeXMLWriter.hpp"
26 #include "deStringUtil.hpp"
27
28 #include <fstream>
29
30 namespace xe
31 {
32
33 /* Batch result writer. */
34
35 struct ContainerValue
36 {
37         ContainerValue (const std::string& value_)      : value(value_) {}
38         ContainerValue (const char* value_)                     : value(value_) {}
39         std::string value;
40 };
41
42 std::ostream& operator<< (std::ostream& stream, const ContainerValue& value)
43 {
44         if (value.value.find(' ') != std::string::npos)
45         {
46                 // Escape.
47                 stream << '"';
48                 for (std::string::const_iterator i = value.value.begin(); i != value.value.end(); i++)
49                 {
50                         if (*i == '"' || *i == '\\')
51                                 stream << '\\';
52                         stream << *i;
53                 }
54                 stream << '"';
55         }
56         else
57                 stream << value.value;
58
59         return stream;
60 }
61
62 static void writeSessionInfo (const SessionInfo& info, std::ostream& stream)
63 {
64         if (!info.releaseName.empty())
65                 stream << "#sessionInfo releaseName " << ContainerValue(info.releaseName) << "\n";
66
67         if (!info.releaseId.empty())
68                 stream << "#sessionInfo releaseId " << ContainerValue(info.releaseId) << "\n";
69
70         if (!info.targetName.empty())
71                 stream << "#sessionInfo targetName " << ContainerValue(info.targetName) << "\n";
72
73         if (!info.candyTargetName.empty())
74                 stream << "#sessionInfo candyTargetName " << ContainerValue(info.candyTargetName) << "\n";
75
76         if (!info.configName.empty())
77                 stream << "#sessionInfo configName " << ContainerValue(info.configName) << "\n";
78
79         if (!info.resultName.empty())
80                 stream << "#sessionInfo resultName " << ContainerValue(info.resultName) << "\n";
81
82         // \note Current format uses unescaped timestamps for some strange reason.
83         if (!info.timestamp.empty())
84                 stream << "#sessionInfo timestamp " << info.timestamp << "\n";
85 }
86
87 static void writeTestCase (const TestCaseResultData& caseData, std::ostream& stream)
88 {
89         stream << "\n#beginTestCaseResult " << caseData.getTestCasePath() << "\n";
90
91         if (caseData.getDataSize() > 0)
92         {
93                 stream.write((const char*)caseData.getData(), caseData.getDataSize());
94
95                 deUint8 lastCh = caseData.getData()[caseData.getDataSize()-1];
96                 if (lastCh != '\n' && lastCh != '\r')
97                         stream << "\n";
98         }
99
100         TestStatusCode dataCode = caseData.getStatusCode();
101         if (dataCode == TESTSTATUSCODE_CRASH    ||
102                 dataCode == TESTSTATUSCODE_TIMEOUT      ||
103                 dataCode == TESTSTATUSCODE_TERMINATED)
104                 stream << "#terminateTestCaseResult " << getTestStatusCodeName(dataCode) << "\n";
105         else
106                 stream << "#endTestCaseResult\n";
107 }
108
109 void writeTestLog (const BatchResult& result, std::ostream& stream)
110 {
111         writeSessionInfo(result.getSessionInfo(), stream);
112
113         stream << "#beginSession\n";
114
115         for (int ndx = 0; ndx < result.getNumTestCaseResults(); ndx++)
116         {
117                 ConstTestCaseResultPtr caseData = result.getTestCaseResult(ndx);
118                 writeTestCase(*caseData, stream);
119         }
120
121         stream << "\n#endSession\n";
122 }
123
124 void writeBatchResultToFile (const BatchResult& result, const char* filename)
125 {
126         std::ofstream str(filename, std::ofstream::binary|std::ofstream::trunc);
127         writeTestLog(result, str);
128         str.close();
129 }
130
131 /* Test result log writer. */
132
133 static const char* getImageFormatName (ri::Image::Format format)
134 {
135         switch (format)
136         {
137                 case ri::Image::FORMAT_RGB888:          return "RGB888";
138                 case ri::Image::FORMAT_RGBA8888:        return "RGBA8888";
139                 default:
140                         DE_ASSERT(false);
141                         return DE_NULL;
142         }
143 }
144
145 static const char* getImageCompressionName (ri::Image::Compression compression)
146 {
147         switch (compression)
148         {
149                 case ri::Image::COMPRESSION_NONE:       return "None";
150                 case ri::Image::COMPRESSION_PNG:        return "PNG";
151                 default:
152                         DE_ASSERT(false);
153                         return DE_NULL;
154         }
155 }
156
157 static const char* getSampleValueTagName (ri::ValueInfo::ValueTag tag)
158 {
159         switch (tag)
160         {
161                 case ri::ValueInfo::VALUETAG_PREDICTOR: return "Predictor";
162                 case ri::ValueInfo::VALUETAG_RESPONSE:  return "Response";
163                 default:
164                         DE_ASSERT(false);
165                         return DE_NULL;
166         }
167 }
168
169 inline const char* getBoolName (bool val)
170 {
171         return val ? "True" : "False";
172 }
173
174 // \todo [2012-09-07 pyry] Move to tcutil?
175 class Base64Formatter
176 {
177 public:
178         const deUint8*  data;
179         int                             numBytes;
180
181         Base64Formatter (const deUint8* data_, int numBytes_) : data(data_), numBytes(numBytes_) {}
182 };
183
184 std::ostream& operator<< (std::ostream& str, const Base64Formatter& fmt)
185 {
186         static const char s_base64Table[64] =
187         {
188                 'A','B','C','D','E','F','G','H','I','J','K','L','M',
189                 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
190                 'a','b','c','d','e','f','g','h','i','j','k','l','m',
191                 'n','o','p','q','r','s','t','u','v','w','x','y','z',
192                 '0','1','2','3','4','5','6','7','8','9','+','/'
193         };
194
195         const deUint8*  data            = fmt.data;
196         int                             numBytes        = fmt.numBytes;
197         int                             srcNdx          = 0;
198
199         DE_ASSERT(data && (numBytes > 0));
200
201         /* Loop all input chars. */
202         while (srcNdx < numBytes)
203         {
204                 int             numRead = de::min(3, numBytes - srcNdx);
205                 deUint8 s0              = data[srcNdx];
206                 deUint8 s1              = (numRead >= 2) ? data[srcNdx+1] : 0;
207                 deUint8 s2              = (numRead >= 3) ? data[srcNdx+2] : 0;
208                 char    d[4];
209
210                 srcNdx += numRead;
211
212                 d[0] = s_base64Table[s0 >> 2];
213                 d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)];
214                 d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)];
215                 d[3] = s_base64Table[s2&0x3F];
216
217                 if (numRead < 3) d[3] = '=';
218                 if (numRead < 2) d[2] = '=';
219
220                 /* Write data. */
221                 str.write(&d[0], sizeof(d));
222         }
223
224         return str;
225 }
226
227 inline Base64Formatter toBase64 (const deUint8* bytes, int numBytes) { return Base64Formatter(bytes, numBytes); }
228
229 static const char* getStatusName (bool value)
230 {
231         return value ? "OK" : "Fail";
232 }
233
234 static void writeResultItem (const ri::Item& item, xml::Writer& dst)
235 {
236         using xml::Writer;
237
238         switch (item.getType())
239         {
240                 case ri::TYPE_RESULT:
241                         // Ignored here, written at end.
242                         break;
243
244                 case ri::TYPE_TEXT:
245                         dst << Writer::BeginElement("Text") << static_cast<const ri::Text&>(item).text << Writer::EndElement;
246                         break;
247
248                 case ri::TYPE_NUMBER:
249                 {
250                         const ri::Number& number = static_cast<const ri::Number&>(item);
251                         dst << Writer::BeginElement("Number")
252                                 << Writer::Attribute("Name",            number.name)
253                                 << Writer::Attribute("Description",     number.description)
254                                 << Writer::Attribute("Unit",            number.unit)
255                                 << Writer::Attribute("Tag",                     number.tag)
256                                 << number.value
257                                 << Writer::EndElement;
258                         break;
259                 }
260
261                 case ri::TYPE_IMAGE:
262                 {
263                         const ri::Image& image = static_cast<const ri::Image&>(item);
264                         dst << Writer::BeginElement("Image")
265                                 << Writer::Attribute("Name",                    image.name)
266                                 << Writer::Attribute("Description",             image.description)
267                                 << Writer::Attribute("Width",                   de::toString(image.width))
268                                 << Writer::Attribute("Height",                  de::toString(image.height))
269                                 << Writer::Attribute("Format",                  getImageFormatName(image.format))
270                                 << Writer::Attribute("CompressionMode", getImageCompressionName(image.compression))
271                                 << toBase64(&image.data[0], (int)image.data.size())
272                                 << Writer::EndElement;
273                         break;
274                 }
275
276                 case ri::TYPE_IMAGESET:
277                 {
278                         const ri::ImageSet& imageSet = static_cast<const ri::ImageSet&>(item);
279                         dst << Writer::BeginElement("ImageSet")
280                                 << Writer::Attribute("Name",            imageSet.name)
281                                 << Writer::Attribute("Description",     imageSet.description);
282
283                         for (int ndx = 0; ndx < imageSet.images.getNumItems(); ndx++)
284                                 writeResultItem(imageSet.images.getItem(ndx), dst);
285
286                         dst << Writer::EndElement;
287                         break;
288                 }
289
290                 case ri::TYPE_SHADER:
291                 {
292                         const ri::Shader&       shader          = static_cast<const ri::Shader&>(item);
293                         const char*                     tagName         = DE_NULL;
294
295                         switch (shader.shaderType)
296                         {
297                                 case ri::Shader::SHADERTYPE_VERTEX:                             tagName = "VertexShader";                       break;
298                                 case ri::Shader::SHADERTYPE_FRAGMENT:                   tagName = "FragmentShader";                     break;
299                                 case ri::Shader::SHADERTYPE_GEOMETRY:                   tagName = "GeometryShader";                     break;
300                                 case ri::Shader::SHADERTYPE_TESS_CONTROL:               tagName = "TessControlShader";          break;
301                                 case ri::Shader::SHADERTYPE_TESS_EVALUATION:    tagName = "TessEvaluationShader";       break;
302                                 case ri::Shader::SHADERTYPE_COMPUTE:                    tagName = "ComputeShader";                      break;
303                                 case ri::Shader::SHADERTYPE_RAYGEN:                             tagName = "RaygenShader";                       break;
304                                 case ri::Shader::SHADERTYPE_ANY_HIT:                    tagName = "AnyHitShader";                       break;
305                                 case ri::Shader::SHADERTYPE_CLOSEST_HIT:                tagName = "ClosestHitShader";           break;
306                                 case ri::Shader::SHADERTYPE_MISS:                               tagName = "MissShader";                         break;
307                                 case ri::Shader::SHADERTYPE_INTERSECTION:               tagName = "IntersectionShader";         break;
308                                 case ri::Shader::SHADERTYPE_CALLABLE:                   tagName = "CallableShader";                     break;
309
310                                 default:
311                                         throw Error("Unknown shader type");
312                         }
313
314                         dst << Writer::BeginElement(tagName)
315                                 << Writer::Attribute("CompileStatus",   getStatusName(shader.compileStatus));
316
317                         writeResultItem(shader.source, dst);
318                         writeResultItem(shader.infoLog, dst);
319
320                         dst << Writer::EndElement;
321                         break;
322                 }
323
324                 case ri::TYPE_SHADERPROGRAM:
325                 {
326                         const ri::ShaderProgram& program = static_cast<const ri::ShaderProgram&>(item);
327                         dst << Writer::BeginElement("ShaderProgram")
328                                 << Writer::Attribute("LinkStatus",      getStatusName(program.linkStatus));
329
330                         writeResultItem(program.linkInfoLog, dst);
331
332                         for (int ndx = 0; ndx < program.shaders.getNumItems(); ndx++)
333                                 writeResultItem(program.shaders.getItem(ndx), dst);
334
335                         dst << Writer::EndElement;
336                         break;
337                 }
338
339                 case ri::TYPE_SHADERSOURCE:
340                         dst << Writer::BeginElement("ShaderSource") << static_cast<const ri::ShaderSource&>(item).source << Writer::EndElement;
341                         break;
342
343                 case ri::TYPE_SPIRVSOURCE:
344                         dst << Writer::BeginElement("SpirVAssemblySource") << static_cast<const ri::SpirVSource&>(item).source << Writer::EndElement;
345                         break;
346
347                 case ri::TYPE_INFOLOG:
348                         dst << Writer::BeginElement("InfoLog") << static_cast<const ri::InfoLog&>(item).log << Writer::EndElement;
349                         break;
350
351                 case ri::TYPE_SECTION:
352                 {
353                         const ri::Section& section = static_cast<const ri::Section&>(item);
354                         dst << Writer::BeginElement("Section")
355                                 << Writer::Attribute("Name",            section.name)
356                                 << Writer::Attribute("Description",     section.description);
357
358                         for (int ndx = 0; ndx < section.items.getNumItems(); ndx++)
359                                 writeResultItem(section.items.getItem(ndx), dst);
360
361                         dst << Writer::EndElement;
362                         break;
363                 }
364
365                 case ri::TYPE_KERNELSOURCE:
366                         dst << Writer::BeginElement("KernelSource") << static_cast<const ri::KernelSource&>(item).source << Writer::EndElement;
367                         break;
368
369                 case ri::TYPE_COMPILEINFO:
370                 {
371                         const ri::CompileInfo& compileInfo = static_cast<const ri::CompileInfo&>(item);
372                         dst << Writer::BeginElement("CompileInfo")
373                                 << Writer::Attribute("Name",                    compileInfo.name)
374                                 << Writer::Attribute("Description",             compileInfo.description)
375                                 << Writer::Attribute("CompileStatus",   getStatusName(compileInfo.compileStatus));
376
377                         writeResultItem(compileInfo.infoLog, dst);
378
379                         dst << Writer::EndElement;
380                         break;
381                 }
382
383                 case ri::TYPE_EGLCONFIG:
384                 {
385                         const ri::EglConfig& config = static_cast<const ri::EglConfig&>(item);
386                         dst << Writer::BeginElement("EglConfig")
387                                 << Writer::Attribute("BufferSize",                              de::toString(config.bufferSize))
388                                 << Writer::Attribute("RedSize",                                 de::toString(config.redSize))
389                                 << Writer::Attribute("GreenSize",                               de::toString(config.greenSize))
390                                 << Writer::Attribute("BlueSize",                                de::toString(config.blueSize))
391                                 << Writer::Attribute("LuminanceSize",                   de::toString(config.luminanceSize))
392                                 << Writer::Attribute("AlphaSize",                               de::toString(config.alphaSize))
393                                 << Writer::Attribute("AlphaMaskSize",                   de::toString(config.alphaMaskSize))
394                                 << Writer::Attribute("BindToTextureRGB",                getBoolName(config.bindToTextureRGB))
395                                 << Writer::Attribute("BindToTextureRGBA",               getBoolName(config.bindToTextureRGBA))
396                                 << Writer::Attribute("ColorBufferType",                 config.colorBufferType)
397                                 << Writer::Attribute("ConfigCaveat",                    config.configCaveat)
398                                 << Writer::Attribute("ConfigID",                                de::toString(config.configID))
399                                 << Writer::Attribute("Conformant",                              config.conformant)
400                                 << Writer::Attribute("DepthSize",                               de::toString(config.depthSize))
401                                 << Writer::Attribute("Level",                                   de::toString(config.level))
402                                 << Writer::Attribute("MaxPBufferWidth",                 de::toString(config.maxPBufferWidth))
403                                 << Writer::Attribute("MaxPBufferHeight",                de::toString(config.maxPBufferHeight))
404                                 << Writer::Attribute("MaxPBufferPixels",                de::toString(config.maxPBufferPixels))
405                                 << Writer::Attribute("MaxSwapInterval",                 de::toString(config.maxSwapInterval))
406                                 << Writer::Attribute("MinSwapInterval",                 de::toString(config.minSwapInterval))
407                                 << Writer::Attribute("NativeRenderable",                getBoolName(config.nativeRenderable))
408                                 << Writer::Attribute("RenderableType",                  config.renderableType)
409                                 << Writer::Attribute("SampleBuffers",                   de::toString(config.sampleBuffers))
410                                 << Writer::Attribute("Samples",                                 de::toString(config.samples))
411                                 << Writer::Attribute("StencilSize",                             de::toString(config.stencilSize))
412                                 << Writer::Attribute("SurfaceTypes",                    config.surfaceTypes)
413                                 << Writer::Attribute("TransparentType",                 config.transparentType)
414                                 << Writer::Attribute("TransparentRedValue",             de::toString(config.transparentRedValue))
415                                 << Writer::Attribute("TransparentGreenValue",   de::toString(config.transparentGreenValue))
416                                 << Writer::Attribute("TransparentBlueValue",    de::toString(config.transparentBlueValue))
417                                 << Writer::EndElement;
418                         break;
419                 }
420
421                 case ri::TYPE_EGLCONFIGSET:
422                 {
423                         const ri::EglConfigSet& configSet = static_cast<const ri::EglConfigSet&>(item);
424                         dst << Writer::BeginElement("EglConfigSet")
425                                 << Writer::Attribute("Name",                    configSet.name)
426                                 << Writer::Attribute("Description",             configSet.description);
427
428                         for (int ndx = 0; ndx < configSet.configs.getNumItems(); ndx++)
429                                 writeResultItem(configSet.configs.getItem(ndx), dst);
430
431                         dst << Writer::EndElement;
432                         break;
433                 }
434
435                 case ri::TYPE_SAMPLELIST:
436                 {
437                         const ri::SampleList& list = static_cast<const ri::SampleList&>(item);
438                         dst << Writer::BeginElement("SampleList")
439                                 << Writer::Attribute("Name",            list.name)
440                                 << Writer::Attribute("Description",     list.description);
441
442                         writeResultItem(list.sampleInfo, dst);
443
444                         for (int ndx = 0; ndx < list.samples.getNumItems(); ndx++)
445                                 writeResultItem(list.samples.getItem(ndx), dst);
446
447                         dst << Writer::EndElement;
448                         break;
449                 }
450
451                 case ri::TYPE_SAMPLEINFO:
452                 {
453                         const ri::SampleInfo& info = static_cast<const ri::SampleInfo&>(item);
454                         dst << Writer::BeginElement("SampleInfo");
455                         for (int ndx = 0; ndx < info.valueInfos.getNumItems(); ndx++)
456                                 writeResultItem(info.valueInfos.getItem(ndx), dst);
457                         dst << Writer::EndElement;
458                         break;
459                 }
460
461                 case ri::TYPE_VALUEINFO:
462                 {
463                         const ri::ValueInfo& info = static_cast<const ri::ValueInfo&>(item);
464                         dst << Writer::BeginElement("ValueInfo")
465                                 << Writer::Attribute("Name",            info.name)
466                                 << Writer::Attribute("Description",     info.description)
467                                 << Writer::Attribute("Tag",                     getSampleValueTagName(info.tag));
468                         if (!info.unit.empty())
469                                 dst << Writer::Attribute("Unit", info.unit);
470                         dst << Writer::EndElement;
471                         break;
472                 }
473
474                 case ri::TYPE_SAMPLE:
475                 {
476                         const ri::Sample& sample = static_cast<const ri::Sample&>(item);
477                         dst << Writer::BeginElement("Sample");
478                         for (int ndx = 0; ndx < sample.values.getNumItems(); ndx++)
479                                 writeResultItem(sample.values.getItem(ndx), dst);
480                         dst << Writer::EndElement;
481                         break;
482                 }
483
484                 case ri::TYPE_SAMPLEVALUE:
485                 {
486                         const ri::SampleValue& value = static_cast<const ri::SampleValue&>(item);
487                         dst << Writer::BeginElement("Value")
488                                 << value.value
489                                 << Writer::EndElement;
490                         break;
491                 }
492
493                 default:
494                         XE_FAIL("Unsupported result item");
495         }
496 }
497
498 void writeTestResult (const TestCaseResult& result, xe::xml::Writer& xmlWriter)
499 {
500         using xml::Writer;
501
502         xmlWriter << Writer::BeginElement("TestCaseResult")
503                           << Writer::Attribute("Version", result.caseVersion)
504                           << Writer::Attribute("CasePath", result.casePath)
505                           << Writer::Attribute("CaseType", getTestCaseTypeName(result.caseType));
506
507         for (int ndx = 0; ndx < result.resultItems.getNumItems(); ndx++)
508                 writeResultItem(result.resultItems.getItem(ndx), xmlWriter);
509
510         // Result item is not logged until end.
511         xmlWriter << Writer::BeginElement("Result")
512                           << Writer::Attribute("StatusCode", getTestStatusCodeName(result.statusCode))
513                           << result.statusDetails
514                           << Writer::EndElement;
515
516         xmlWriter << Writer::EndElement;
517 }
518
519 void writeTestResult (const TestCaseResult& result, std::ostream& stream)
520 {
521         xml::Writer xmlWriter(stream);
522         stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
523         writeTestResult(result, xmlWriter);
524 }
525
526 void writeTestResultToFile (const TestCaseResult& result, const char* filename)
527 {
528         std::ofstream str(filename, std::ofstream::binary|std::ofstream::trunc);
529         writeTestResult(result, str);
530         str.close();
531 }
532
533 } // xe