xref: /aosp_15_r20/external/deqp/executor/tools/xeBatchResultToXml.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 Batch result to XML export.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "xeTestLogParser.hpp"
25 #include "xeTestResultParser.hpp"
26 #include "xeXMLWriter.hpp"
27 #include "xeTestLogWriter.hpp"
28 #include "deFilePath.hpp"
29 #include "deString.h"
30 #include "deStringUtil.hpp"
31 #include "deCommandLine.hpp"
32 
33 #include <vector>
34 #include <string>
35 #include <map>
36 #include <cstdio>
37 #include <fstream>
38 #include <iostream>
39 
40 using std::map;
41 using std::string;
42 using std::vector;
43 
44 static const char *CASELIST_STYLESHEET = "caselist.xsl";
45 static const char *TESTCASE_STYLESHEET = "testlog.xsl";
46 
47 enum OutputMode
48 {
49     OUTPUTMODE_SEPARATE = 0, //!< Separate
50     OUTPUTMODE_SINGLE,
51 
52     OUTPUTMODE_LAST
53 };
54 
55 namespace opt
56 {
57 
58 DE_DECLARE_COMMAND_LINE_OPT(OutMode, OutputMode);
59 
registerOptions(de::cmdline::Parser & parser)60 void registerOptions(de::cmdline::Parser &parser)
61 {
62     using de::cmdline::NamedValue;
63     using de::cmdline::Option;
64 
65     static const NamedValue<OutputMode> s_modes[] = {{"single", OUTPUTMODE_SINGLE}, {"separate", OUTPUTMODE_SEPARATE}};
66 
67     parser << Option<OutMode>("m", "mode", "Output mode", s_modes, "single");
68 }
69 
70 } // namespace opt
71 
72 struct CommandLine
73 {
CommandLineCommandLine74     CommandLine(void) : outputMode(OUTPUTMODE_SINGLE)
75     {
76     }
77 
78     std::string batchResultFile;
79     std::string outputPath;
80     OutputMode outputMode;
81 };
82 
parseCommandLine(CommandLine & cmdLine,int argc,const char * const * argv)83 static bool parseCommandLine(CommandLine &cmdLine, int argc, const char *const *argv)
84 {
85     de::cmdline::Parser parser;
86     de::cmdline::CommandLine opts;
87 
88     opt::registerOptions(parser);
89 
90     if (!parser.parse(argc - 1, argv + 1, &opts, std::cerr) || opts.getArgs().size() != 2)
91     {
92         printf("%s: [options] [testlog] [destination path]\n", argv[0]);
93         parser.help(std::cout);
94         return false;
95     }
96 
97     cmdLine.outputMode      = opts.getOption<opt::OutMode>();
98     cmdLine.batchResultFile = opts.getArgs()[0];
99     cmdLine.outputPath      = opts.getArgs()[1];
100 
101     return true;
102 }
103 
parseBatchResult(xe::TestLogParser & parser,const char * filename)104 static void parseBatchResult(xe::TestLogParser &parser, const char *filename)
105 {
106     std::ifstream in(filename, std::ios_base::binary);
107     uint8_t buf[2048];
108 
109     for (;;)
110     {
111         in.read((char *)&buf[0], sizeof(buf));
112         int numRead = (int)in.gcount();
113 
114         if (numRead > 0)
115             parser.parse(&buf[0], numRead);
116 
117         if (numRead < (int)sizeof(buf))
118             break;
119     }
120 }
121 
122 // Export to single file
123 
124 struct BatchResultTotals
125 {
BatchResultTotalsBatchResultTotals126     BatchResultTotals(void)
127     {
128         for (int i = 0; i < xe::TESTSTATUSCODE_LAST; i++)
129             countByCode[i] = 0;
130     }
131 
132     int countByCode[xe::TESTSTATUSCODE_LAST];
133 };
134 
135 class ResultToSingleXmlLogHandler : public xe::TestLogHandler
136 {
137 public:
ResultToSingleXmlLogHandler(xe::xml::Writer & writer,BatchResultTotals & totals)138     ResultToSingleXmlLogHandler(xe::xml::Writer &writer, BatchResultTotals &totals) : m_writer(writer), m_totals(totals)
139     {
140     }
141 
setSessionInfo(const xe::SessionInfo &)142     void setSessionInfo(const xe::SessionInfo &)
143     {
144     }
145 
startTestCaseResult(const char * casePath)146     xe::TestCaseResultPtr startTestCaseResult(const char *casePath)
147     {
148         return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
149     }
150 
testCaseResultUpdated(const xe::TestCaseResultPtr &)151     void testCaseResultUpdated(const xe::TestCaseResultPtr &)
152     {
153     }
154 
testCaseResultComplete(const xe::TestCaseResultPtr & resultData)155     void testCaseResultComplete(const xe::TestCaseResultPtr &resultData)
156     {
157         xe::TestCaseResult result;
158 
159         xe::parseTestCaseResultFromData(&m_resultParser, &result, *resultData.get());
160 
161         // Write result.
162         xe::writeTestResult(result, m_writer);
163 
164         // Record total
165         XE_CHECK(de::inBounds<int>(result.statusCode, 0, xe::TESTSTATUSCODE_LAST));
166         m_totals.countByCode[result.statusCode] += 1;
167     }
168 
169 private:
170     xe::xml::Writer &m_writer;
171     BatchResultTotals &m_totals;
172     xe::TestResultParser m_resultParser;
173 };
174 
writeTotals(xe::xml::Writer & writer,const BatchResultTotals & totals)175 static void writeTotals(xe::xml::Writer &writer, const BatchResultTotals &totals)
176 {
177     using xe::xml::Writer;
178 
179     int totalCases = 0;
180 
181     writer << Writer::BeginElement("ResultTotals");
182 
183     for (int code = 0; code < xe::TESTSTATUSCODE_LAST; code++)
184     {
185         writer << Writer::Attribute(xe::getTestStatusCodeName((xe::TestStatusCode)code),
186                                     de::toString(totals.countByCode[code]).c_str());
187         totalCases += totals.countByCode[code];
188     }
189 
190     writer << Writer::Attribute("All", de::toString(totalCases).c_str()) << Writer::EndElement;
191 }
192 
batchResultToSingleXmlFile(const char * batchResultFilename,const char * dstFileName)193 static void batchResultToSingleXmlFile(const char *batchResultFilename, const char *dstFileName)
194 {
195     std::ofstream out(dstFileName, std::ios_base::binary);
196     xe::xml::Writer writer(out);
197     BatchResultTotals totals;
198     ResultToSingleXmlLogHandler handler(writer, totals);
199     xe::TestLogParser parser(&handler);
200 
201     XE_CHECK(out.good());
202 
203     out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
204         << "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\n";
205 
206     writer << xe::xml::Writer::BeginElement("BatchResult")
207            << xe::xml::Writer::Attribute("FileName", de::FilePath(batchResultFilename).getBaseName());
208 
209     // Parse and write individual cases
210     parseBatchResult(parser, batchResultFilename);
211 
212     // Write ResultTotals
213     writeTotals(writer, totals);
214 
215     writer << xe::xml::Writer::EndElement;
216     out << "\n";
217 }
218 
219 // Export to separate files
220 
221 class ResultToXmlFilesLogHandler : public xe::TestLogHandler
222 {
223 public:
ResultToXmlFilesLogHandler(vector<xe::TestCaseResultHeader> & resultHeaders,const char * dstPath)224     ResultToXmlFilesLogHandler(vector<xe::TestCaseResultHeader> &resultHeaders, const char *dstPath)
225         : m_resultHeaders(resultHeaders)
226         , m_dstPath(dstPath)
227     {
228     }
229 
setSessionInfo(const xe::SessionInfo &)230     void setSessionInfo(const xe::SessionInfo &)
231     {
232     }
233 
startTestCaseResult(const char * casePath)234     xe::TestCaseResultPtr startTestCaseResult(const char *casePath)
235     {
236         return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
237     }
238 
testCaseResultUpdated(const xe::TestCaseResultPtr &)239     void testCaseResultUpdated(const xe::TestCaseResultPtr &)
240     {
241     }
242 
testCaseResultComplete(const xe::TestCaseResultPtr & resultData)243     void testCaseResultComplete(const xe::TestCaseResultPtr &resultData)
244     {
245         xe::TestCaseResult result;
246 
247         xe::parseTestCaseResultFromData(&m_resultParser, &result, *resultData.get());
248 
249         // Write result.
250         {
251             de::FilePath casePath = de::FilePath::join(m_dstPath, (result.casePath + ".xml").c_str());
252             std::ofstream out(casePath.getPath(), std::ofstream::binary | std::ofstream::trunc);
253             xe::xml::Writer xmlWriter(out);
254 
255             if (!out.good())
256                 throw xe::Error(string("Failed to open ") + casePath.getPath());
257 
258             out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
259                 << "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\n";
260             xe::writeTestResult(result, xmlWriter);
261             out << "\n";
262         }
263 
264         m_resultHeaders.push_back(xe::TestCaseResultHeader(result));
265     }
266 
267 private:
268     vector<xe::TestCaseResultHeader> &m_resultHeaders;
269     std::string m_dstPath;
270     xe::TestResultParser m_resultParser;
271 };
272 
273 typedef std::map<const xe::TestCase *, const xe::TestCaseResultHeader *> ShortTestResultMap;
274 
writeTestCaseListNode(const xe::TestNode * testNode,const ShortTestResultMap & resultMap,xe::xml::Writer & dst)275 static void writeTestCaseListNode(const xe::TestNode *testNode, const ShortTestResultMap &resultMap,
276                                   xe::xml::Writer &dst)
277 {
278     using xe::xml::Writer;
279 
280     bool isGroup = testNode->getNodeType() == xe::TESTNODETYPE_GROUP;
281     string fullPath;
282     testNode->getFullPath(fullPath);
283 
284     if (isGroup)
285     {
286         const xe::TestGroup *group = static_cast<const xe::TestGroup *>(testNode);
287 
288         dst << Writer::BeginElement("TestGroup") << Writer::Attribute("Name", testNode->getName());
289 
290         for (int childNdx = 0; childNdx < group->getNumChildren(); childNdx++)
291             writeTestCaseListNode(group->getChild(childNdx), resultMap, dst);
292 
293         dst << Writer::EndElement;
294     }
295     else
296     {
297         DE_ASSERT(testNode->getNodeType() == xe::TESTNODETYPE_TEST_CASE);
298 
299         const xe::TestCase *testCase                 = static_cast<const xe::TestCase *>(testNode);
300         ShortTestResultMap::const_iterator resultPos = resultMap.find(testCase);
301         const xe::TestCaseResultHeader *result       = resultPos != resultMap.end() ? resultPos->second : DE_NULL;
302 
303         DE_ASSERT(result);
304 
305         dst << Writer::BeginElement("TestCase") << Writer::Attribute("Name", testNode->getName())
306             << Writer::Attribute("Type", xe::getTestCaseTypeName(result->caseType))
307             << Writer::Attribute("StatusCode", xe::getTestStatusCodeName(result->statusCode))
308             << Writer::Attribute("StatusDetails", result->statusDetails.c_str()) << Writer::EndElement;
309     }
310 }
311 
writeTestCaseList(const xe::TestRoot & root,const ShortTestResultMap & resultMap,xe::xml::Writer & dst)312 static void writeTestCaseList(const xe::TestRoot &root, const ShortTestResultMap &resultMap, xe::xml::Writer &dst)
313 {
314     using xe::xml::Writer;
315 
316     dst << Writer::BeginElement("TestRoot");
317 
318     for (int childNdx = 0; childNdx < root.getNumChildren(); childNdx++)
319         writeTestCaseListNode(root.getChild(childNdx), resultMap, dst);
320 
321     dst << Writer::EndElement;
322 }
323 
batchResultToSeparateXmlFiles(const char * batchResultFilename,const char * dstPath)324 static void batchResultToSeparateXmlFiles(const char *batchResultFilename, const char *dstPath)
325 {
326     xe::TestRoot testRoot;
327     vector<xe::TestCaseResultHeader> shortResults;
328     ShortTestResultMap resultMap;
329 
330     // Initialize destination directory.
331     if (!de::FilePath(dstPath).exists())
332         de::createDirectoryAndParents(dstPath);
333     else
334         XE_CHECK_MSG(de::FilePath(dstPath).getType() == de::FilePath::TYPE_DIRECTORY, "Destination is not directory");
335 
336     // Parse batch result and write out test cases.
337     {
338         ResultToXmlFilesLogHandler handler(shortResults, dstPath);
339         xe::TestLogParser parser(&handler);
340 
341         parseBatchResult(parser, batchResultFilename);
342     }
343 
344     // Build case hierarchy & short result map.
345     {
346         xe::TestHierarchyBuilder hierarchyBuilder(&testRoot);
347 
348         for (vector<xe::TestCaseResultHeader>::const_iterator result = shortResults.begin();
349              result != shortResults.end(); result++)
350         {
351             xe::TestCase *testCase = hierarchyBuilder.createCase(result->casePath.c_str(), result->caseType);
352             resultMap.insert(std::make_pair(testCase, &(*result)));
353         }
354     }
355 
356     // Create caselist.
357     {
358         de::FilePath indexPath = de::FilePath::join(dstPath, "caselist.xml");
359         std::ofstream out(indexPath.getPath(), std::ofstream::binary | std::ofstream::trunc);
360         xe::xml::Writer xmlWriter(out);
361 
362         XE_CHECK_MSG(out.good(), "Failed to open caselist.xml");
363 
364         out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
365             << "<?xml-stylesheet href=\"" << CASELIST_STYLESHEET << "\" type=\"text/xsl\"?>\n";
366         writeTestCaseList(testRoot, resultMap, xmlWriter);
367         out << "\n";
368     }
369 }
370 
main(int argc,const char * const * argv)371 int main(int argc, const char *const *argv)
372 {
373     try
374     {
375         CommandLine cmdLine;
376         if (!parseCommandLine(cmdLine, argc, argv))
377             return -1;
378 
379         if (cmdLine.outputMode == OUTPUTMODE_SINGLE)
380             batchResultToSingleXmlFile(cmdLine.batchResultFile.c_str(), cmdLine.outputPath.c_str());
381         else
382             batchResultToSeparateXmlFiles(cmdLine.batchResultFile.c_str(), cmdLine.outputPath.c_str());
383     }
384     catch (const std::exception &e)
385     {
386         printf("%s\n", e.what());
387         return -1;
388     }
389 
390     return 0;
391 }
392