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