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