Merge vk-gl-cts/opengl-cts-4.6.2 into vk-gl-cts/main
[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                                 case ri::Shader::SHADERTYPE_TASK:                               tagName = "TaskShader";                         break;
310                                 case ri::Shader::SHADERTYPE_MESH:                               tagName = "MeshShader";                         break;
311
312                                 default:
313                                         throw Error("Unknown shader type");
314                         }
315
316                         dst << Writer::BeginElement(tagName)
317                                 << Writer::Attribute("CompileStatus",   getStatusName(shader.compileStatus));
318
319                         writeResultItem(shader.source, dst);
320                         writeResultItem(shader.infoLog, dst);
321
322                         dst << Writer::EndElement;
323                         break;
324                 }
325
326                 case ri::TYPE_SHADERPROGRAM:
327                 {
328                         const ri::ShaderProgram& program = static_cast<const ri::ShaderProgram&>(item);
329                         dst << Writer::BeginElement("ShaderProgram")
330                                 << Writer::Attribute("LinkStatus",      getStatusName(program.linkStatus));
331
332                         writeResultItem(program.linkInfoLog, dst);
333
334                         for (int ndx = 0; ndx < program.shaders.getNumItems(); ndx++)
335                                 writeResultItem(program.shaders.getItem(ndx), dst);
336
337                         dst << Writer::EndElement;
338                         break;
339                 }
340
341                 case ri::TYPE_SHADERSOURCE:
342                         dst << Writer::BeginElement("ShaderSource") << static_cast<const ri::ShaderSource&>(item).source << Writer::EndElement;
343                         break;
344
345                 case ri::TYPE_SPIRVSOURCE:
346                         dst << Writer::BeginElement("SpirVAssemblySource") << static_cast<const ri::SpirVSource&>(item).source << Writer::EndElement;
347                         break;
348
349                 case ri::TYPE_INFOLOG:
350                         dst << Writer::BeginElement("InfoLog") << static_cast<const ri::InfoLog&>(item).log << Writer::EndElement;
351                         break;
352
353                 case ri::TYPE_SECTION:
354                 {
355                         const ri::Section& section = static_cast<const ri::Section&>(item);
356                         dst << Writer::BeginElement("Section")
357                                 << Writer::Attribute("Name",            section.name)
358                                 << Writer::Attribute("Description",     section.description);
359
360                         for (int ndx = 0; ndx < section.items.getNumItems(); ndx++)
361                                 writeResultItem(section.items.getItem(ndx), dst);
362
363                         dst << Writer::EndElement;
364                         break;
365                 }
366
367                 case ri::TYPE_KERNELSOURCE:
368                         dst << Writer::BeginElement("KernelSource") << static_cast<const ri::KernelSource&>(item).source << Writer::EndElement;
369                         break;
370
371                 case ri::TYPE_COMPILEINFO:
372                 {
373                         const ri::CompileInfo& compileInfo = static_cast<const ri::CompileInfo&>(item);
374                         dst << Writer::BeginElement("CompileInfo")
375                                 << Writer::Attribute("Name",                    compileInfo.name)
376                                 << Writer::Attribute("Description",             compileInfo.description)
377                                 << Writer::Attribute("CompileStatus",   getStatusName(compileInfo.compileStatus));
378
379                         writeResultItem(compileInfo.infoLog, dst);
380
381                         dst << Writer::EndElement;
382                         break;
383                 }
384
385                 case ri::TYPE_EGLCONFIG:
386                 {
387                         const ri::EglConfig& config = static_cast<const ri::EglConfig&>(item);
388                         dst << Writer::BeginElement("EglConfig")
389                                 << Writer::Attribute("BufferSize",                              de::toString(config.bufferSize))
390                                 << Writer::Attribute("RedSize",                                 de::toString(config.redSize))
391                                 << Writer::Attribute("GreenSize",                               de::toString(config.greenSize))
392                                 << Writer::Attribute("BlueSize",                                de::toString(config.blueSize))
393                                 << Writer::Attribute("LuminanceSize",                   de::toString(config.luminanceSize))
394                                 << Writer::Attribute("AlphaSize",                               de::toString(config.alphaSize))
395                                 << Writer::Attribute("AlphaMaskSize",                   de::toString(config.alphaMaskSize))
396                                 << Writer::Attribute("BindToTextureRGB",                getBoolName(config.bindToTextureRGB))
397                                 << Writer::Attribute("BindToTextureRGBA",               getBoolName(config.bindToTextureRGBA))
398                                 << Writer::Attribute("ColorBufferType",                 config.colorBufferType)
399                                 << Writer::Attribute("ConfigCaveat",                    config.configCaveat)
400                                 << Writer::Attribute("ConfigID",                                de::toString(config.configID))
401                                 << Writer::Attribute("Conformant",                              config.conformant)
402                                 << Writer::Attribute("DepthSize",                               de::toString(config.depthSize))
403                                 << Writer::Attribute("Level",                                   de::toString(config.level))
404                                 << Writer::Attribute("MaxPBufferWidth",                 de::toString(config.maxPBufferWidth))
405                                 << Writer::Attribute("MaxPBufferHeight",                de::toString(config.maxPBufferHeight))
406                                 << Writer::Attribute("MaxPBufferPixels",                de::toString(config.maxPBufferPixels))
407                                 << Writer::Attribute("MaxSwapInterval",                 de::toString(config.maxSwapInterval))
408                                 << Writer::Attribute("MinSwapInterval",                 de::toString(config.minSwapInterval))
409                                 << Writer::Attribute("NativeRenderable",                getBoolName(config.nativeRenderable))
410                                 << Writer::Attribute("RenderableType",                  config.renderableType)
411                                 << Writer::Attribute("SampleBuffers",                   de::toString(config.sampleBuffers))
412                                 << Writer::Attribute("Samples",                                 de::toString(config.samples))
413                                 << Writer::Attribute("StencilSize",                             de::toString(config.stencilSize))
414                                 << Writer::Attribute("SurfaceTypes",                    config.surfaceTypes)
415                                 << Writer::Attribute("TransparentType",                 config.transparentType)
416                                 << Writer::Attribute("TransparentRedValue",             de::toString(config.transparentRedValue))
417                                 << Writer::Attribute("TransparentGreenValue",   de::toString(config.transparentGreenValue))
418                                 << Writer::Attribute("TransparentBlueValue",    de::toString(config.transparentBlueValue))
419                                 << Writer::EndElement;
420                         break;
421                 }
422
423                 case ri::TYPE_EGLCONFIGSET:
424                 {
425                         const ri::EglConfigSet& configSet = static_cast<const ri::EglConfigSet&>(item);
426                         dst << Writer::BeginElement("EglConfigSet")
427                                 << Writer::Attribute("Name",                    configSet.name)
428                                 << Writer::Attribute("Description",             configSet.description);
429
430                         for (int ndx = 0; ndx < configSet.configs.getNumItems(); ndx++)
431                                 writeResultItem(configSet.configs.getItem(ndx), dst);
432
433                         dst << Writer::EndElement;
434                         break;
435                 }
436
437                 case ri::TYPE_SAMPLELIST:
438                 {
439                         const ri::SampleList& list = static_cast<const ri::SampleList&>(item);
440                         dst << Writer::BeginElement("SampleList")
441                                 << Writer::Attribute("Name",            list.name)
442                                 << Writer::Attribute("Description",     list.description);
443
444                         writeResultItem(list.sampleInfo, dst);
445
446                         for (int ndx = 0; ndx < list.samples.getNumItems(); ndx++)
447                                 writeResultItem(list.samples.getItem(ndx), dst);
448
449                         dst << Writer::EndElement;
450                         break;
451                 }
452
453                 case ri::TYPE_SAMPLEINFO:
454                 {
455                         const ri::SampleInfo& info = static_cast<const ri::SampleInfo&>(item);
456                         dst << Writer::BeginElement("SampleInfo");
457                         for (int ndx = 0; ndx < info.valueInfos.getNumItems(); ndx++)
458                                 writeResultItem(info.valueInfos.getItem(ndx), dst);
459                         dst << Writer::EndElement;
460                         break;
461                 }
462
463                 case ri::TYPE_VALUEINFO:
464                 {
465                         const ri::ValueInfo& info = static_cast<const ri::ValueInfo&>(item);
466                         dst << Writer::BeginElement("ValueInfo")
467                                 << Writer::Attribute("Name",            info.name)
468                                 << Writer::Attribute("Description",     info.description)
469                                 << Writer::Attribute("Tag",                     getSampleValueTagName(info.tag));
470                         if (!info.unit.empty())
471                                 dst << Writer::Attribute("Unit", info.unit);
472                         dst << Writer::EndElement;
473                         break;
474                 }
475
476                 case ri::TYPE_SAMPLE:
477                 {
478                         const ri::Sample& sample = static_cast<const ri::Sample&>(item);
479                         dst << Writer::BeginElement("Sample");
480                         for (int ndx = 0; ndx < sample.values.getNumItems(); ndx++)
481                                 writeResultItem(sample.values.getItem(ndx), dst);
482                         dst << Writer::EndElement;
483                         break;
484                 }
485
486                 case ri::TYPE_SAMPLEVALUE:
487                 {
488                         const ri::SampleValue& value = static_cast<const ri::SampleValue&>(item);
489                         dst << Writer::BeginElement("Value")
490                                 << value.value
491                                 << Writer::EndElement;
492                         break;
493                 }
494
495                 default:
496                         XE_FAIL("Unsupported result item");
497         }
498 }
499
500 void writeTestResult (const TestCaseResult& result, xe::xml::Writer& xmlWriter)
501 {
502         using xml::Writer;
503
504         xmlWriter << Writer::BeginElement("TestCaseResult")
505                           << Writer::Attribute("Version", result.caseVersion)
506                           << Writer::Attribute("CasePath", result.casePath)
507                           << Writer::Attribute("CaseType", getTestCaseTypeName(result.caseType));
508
509         for (int ndx = 0; ndx < result.resultItems.getNumItems(); ndx++)
510                 writeResultItem(result.resultItems.getItem(ndx), xmlWriter);
511
512         // Result item is not logged until end.
513         xmlWriter << Writer::BeginElement("Result")
514                           << Writer::Attribute("StatusCode", getTestStatusCodeName(result.statusCode))
515                           << result.statusDetails
516                           << Writer::EndElement;
517
518         xmlWriter << Writer::EndElement;
519 }
520
521 void writeTestResult (const TestCaseResult& result, std::ostream& stream)
522 {
523         xml::Writer xmlWriter(stream);
524         stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
525         writeTestResult(result, xmlWriter);
526 }
527
528 void writeTestResultToFile (const TestCaseResult& result, const char* filename)
529 {
530         std::ofstream str(filename, std::ofstream::binary|std::ofstream::trunc);
531         writeTestResult(result, str);
532         str.close();
533 }
534
535 } // xe