1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Test Executor
3 * ------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Batch result to XML export.
22 *//*--------------------------------------------------------------------*/
24 #include "xeTestLogParser.hpp"
25 #include "xeTestResultParser.hpp"
26 #include "xeXMLWriter.hpp"
27 #include "xeTestLogWriter.hpp"
28 #include "deFilePath.hpp"
30 #include "deStringUtil.hpp"
31 #include "deCommandLine.hpp"
44 static const char* CASELIST_STYLESHEET = "caselist.xsl";
45 static const char* TESTCASE_STYLESHEET = "testlog.xsl";
49 OUTPUTMODE_SEPARATE = 0, //!< Separate
58 DE_DECLARE_COMMAND_LINE_OPT(OutMode, OutputMode);
60 void registerOptions (de::cmdline::Parser& parser)
62 using de::cmdline::Option;
63 using de::cmdline::NamedValue;
65 static const NamedValue<OutputMode> s_modes[] =
67 { "single", OUTPUTMODE_SINGLE },
68 { "separate", OUTPUTMODE_SEPARATE }
71 parser << Option<OutMode>("m", "mode", "Output mode", s_modes, "single");
79 : outputMode(OUTPUTMODE_SINGLE)
83 std::string batchResultFile;
84 std::string outputPath;
85 OutputMode outputMode;
88 static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
90 de::cmdline::Parser parser;
91 de::cmdline::CommandLine opts;
93 opt::registerOptions(parser);
95 if (!parser.parse(argc-1, argv+1, &opts, std::cerr) ||
96 opts.getArgs().size() != 2)
98 printf("%s: [options] [testlog] [destination path]\n", argv[0]);
99 parser.help(std::cout);
103 cmdLine.outputMode = opts.getOption<opt::OutMode>();
104 cmdLine.batchResultFile = opts.getArgs()[0];
105 cmdLine.outputPath = opts.getArgs()[1];
110 static void parseBatchResult (xe::TestLogParser& parser, const char* filename)
112 std::ifstream in (filename, std::ios_base::binary);
117 in.read((char*)&buf[0], sizeof(buf));
118 int numRead = (int)in.gcount();
121 parser.parse(&buf[0], numRead);
123 if (numRead < (int)sizeof(buf))
128 // Export to single file
130 struct BatchResultTotals
132 BatchResultTotals (void)
134 for (int i = 0;i < xe::TESTSTATUSCODE_LAST; i++)
138 int countByCode[xe::TESTSTATUSCODE_LAST];
141 class ResultToSingleXmlLogHandler : public xe::TestLogHandler
144 ResultToSingleXmlLogHandler (xe::xml::Writer& writer, BatchResultTotals& totals)
150 void setSessionInfo (const xe::SessionInfo&)
154 xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
156 return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
159 void testCaseResultUpdated (const xe::TestCaseResultPtr&)
163 void testCaseResultComplete (const xe::TestCaseResultPtr& resultData)
165 xe::TestCaseResult result;
167 xe::parseTestCaseResultFromData(&m_resultParser, &result, *resultData.get());
170 xe::writeTestResult(result, m_writer);
173 XE_CHECK(de::inBounds<int>(result.statusCode, 0, xe::TESTSTATUSCODE_LAST));
174 m_totals.countByCode[result.statusCode] += 1;
178 xe::xml::Writer& m_writer;
179 BatchResultTotals& m_totals;
180 xe::TestResultParser m_resultParser;
183 static void writeTotals (xe::xml::Writer& writer, const BatchResultTotals& totals)
185 using xe::xml::Writer;
189 writer << Writer::BeginElement("ResultTotals");
191 for (int code = 0; code < xe::TESTSTATUSCODE_LAST; code++)
193 writer << Writer::Attribute(xe::getTestStatusCodeName((xe::TestStatusCode)code), de::toString(totals.countByCode[code]).c_str());
194 totalCases += totals.countByCode[code];
197 writer << Writer::Attribute("All", de::toString(totalCases).c_str())
198 << Writer::EndElement;
201 static void batchResultToSingleXmlFile (const char* batchResultFilename, const char* dstFileName)
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);
209 XE_CHECK(out.good());
211 out << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
212 << "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\n";
214 writer << xe::xml::Writer::BeginElement("BatchResult")
215 << xe::xml::Writer::Attribute("FileName", de::FilePath(batchResultFilename).getBaseName());
217 // Parse and write individual cases
218 parseBatchResult(parser, batchResultFilename);
220 // Write ResultTotals
221 writeTotals(writer, totals);
223 writer << xe::xml::Writer::EndElement;
227 // Export to separate files
229 class ResultToXmlFilesLogHandler : public xe::TestLogHandler
232 ResultToXmlFilesLogHandler (vector<xe::TestCaseResultHeader>& resultHeaders, const char* dstPath)
233 : m_resultHeaders (resultHeaders)
234 , m_dstPath (dstPath)
238 void setSessionInfo (const xe::SessionInfo&)
242 xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
244 return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
247 void testCaseResultUpdated (const xe::TestCaseResultPtr&)
251 void testCaseResultComplete (const xe::TestCaseResultPtr& resultData)
253 xe::TestCaseResult result;
255 xe::parseTestCaseResultFromData(&m_resultParser, &result, *resultData.get());
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);
264 throw xe::Error(string("Failed to open ") + casePath.getPath());
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);
272 m_resultHeaders.push_back(xe::TestCaseResultHeader(result));
276 vector<xe::TestCaseResultHeader>& m_resultHeaders;
277 std::string m_dstPath;
278 xe::TestResultParser m_resultParser;
281 typedef std::map<const xe::TestCase*, const xe::TestCaseResultHeader*> ShortTestResultMap;
283 static void writeTestCaseListNode (const xe::TestNode* testNode, const ShortTestResultMap& resultMap, xe::xml::Writer& dst)
285 using xe::xml::Writer;
287 bool isGroup = testNode->getNodeType() == xe::TESTNODETYPE_GROUP;
289 testNode->getFullPath(fullPath);
293 const xe::TestGroup* group = static_cast<const xe::TestGroup*>(testNode);
295 dst << Writer::BeginElement("TestGroup")
296 << Writer::Attribute("Name", testNode->getName());
298 for (int childNdx = 0; childNdx < group->getNumChildren(); childNdx++)
299 writeTestCaseListNode(group->getChild(childNdx), resultMap, dst);
301 dst << Writer::EndElement;
305 DE_ASSERT(testNode->getNodeType() == xe::TESTNODETYPE_TEST_CASE);
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;
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;
322 static void writeTestCaseList (const xe::TestRoot& root, const ShortTestResultMap& resultMap, xe::xml::Writer& dst)
324 using xe::xml::Writer;
326 dst << Writer::BeginElement("TestRoot");
328 for (int childNdx = 0; childNdx < root.getNumChildren(); childNdx++)
329 writeTestCaseListNode(root.getChild(childNdx), resultMap, dst);
331 dst << Writer::EndElement;
334 static void batchResultToSeparateXmlFiles (const char* batchResultFilename, const char* dstPath)
336 xe::TestRoot testRoot;
337 vector<xe::TestCaseResultHeader> shortResults;
338 ShortTestResultMap resultMap;
340 // Initialize destination directory.
341 if (!de::FilePath(dstPath).exists())
342 de::createDirectoryAndParents(dstPath);
344 XE_CHECK_MSG(de::FilePath(dstPath).getType() == de::FilePath::TYPE_DIRECTORY, "Destination is not directory");
346 // Parse batch result and write out test cases.
348 ResultToXmlFilesLogHandler handler (shortResults, dstPath);
349 xe::TestLogParser parser (&handler);
351 parseBatchResult(parser, batchResultFilename);
354 // Build case hierarchy & short result map.
356 xe::TestHierarchyBuilder hierarchyBuilder(&testRoot);
358 for (vector<xe::TestCaseResultHeader>::const_iterator result = shortResults.begin(); result != shortResults.end(); result++)
360 xe::TestCase* testCase = hierarchyBuilder.createCase(result->casePath.c_str(), result->caseType);
361 resultMap.insert(std::make_pair(testCase, &(*result)));
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);
371 XE_CHECK_MSG(out.good(), "Failed to open caselist.xml");
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);
380 int main (int argc, const char* const* argv)
385 if (!parseCommandLine(cmdLine, argc, argv))
388 if (cmdLine.outputMode == OUTPUTMODE_SINGLE)
389 batchResultToSingleXmlFile(cmdLine.batchResultFile.c_str(), cmdLine.outputPath.c_str());
391 batchResultToSeparateXmlFiles(cmdLine.batchResultFile.c_str(), cmdLine.outputPath.c_str());
393 catch (const std::exception& e)
395 printf("%s\n", e.what());