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