/*------------------------------------------------------------------------- * drawElements Quality Program Test Executor * ------------------------------------------ * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Batch result to XML export. *//*--------------------------------------------------------------------*/ #include "xeTestLogParser.hpp" #include "xeTestResultParser.hpp" #include "xeXMLWriter.hpp" #include "xeTestLogWriter.hpp" #include "deFilePath.hpp" #include "deString.h" #include "deStringUtil.hpp" #include "deCommandLine.hpp" #include #include #include #include #include #include using std::vector; using std::string; using std::map; static const char* CASELIST_STYLESHEET = "caselist.xsl"; static const char* TESTCASE_STYLESHEET = "testlog.xsl"; enum OutputMode { OUTPUTMODE_SEPARATE = 0, //!< Separate OUTPUTMODE_SINGLE, OUTPUTMODE_LAST }; namespace opt { DE_DECLARE_COMMAND_LINE_OPT(OutMode, OutputMode); void registerOptions (de::cmdline::Parser& parser) { using de::cmdline::Option; using de::cmdline::NamedValue; static const NamedValue s_modes[] = { { "single", OUTPUTMODE_SINGLE }, { "separate", OUTPUTMODE_SEPARATE } }; parser << Option("m", "mode", "Output mode", s_modes, "single"); } } // opt struct CommandLine { CommandLine (void) : outputMode(OUTPUTMODE_SINGLE) { } std::string batchResultFile; std::string outputPath; OutputMode outputMode; }; static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv) { de::cmdline::Parser parser; de::cmdline::CommandLine opts; opt::registerOptions(parser); if (!parser.parse(argc-1, argv+1, &opts, std::cerr) || opts.getArgs().size() != 2) { printf("%s: [options] [testlog] [destination path]\n", argv[0]); parser.help(std::cout); return false; } cmdLine.outputMode = opts.getOption(); cmdLine.batchResultFile = opts.getArgs()[0]; cmdLine.outputPath = opts.getArgs()[1]; return true; } static void parseBatchResult (xe::TestLogParser& parser, const char* filename) { std::ifstream in (filename, std::ios_base::binary); deUint8 buf[2048]; for (;;) { in.read((char*)&buf[0], sizeof(buf)); int numRead = (int)in.gcount(); if (numRead > 0) parser.parse(&buf[0], numRead); if (numRead < (int)sizeof(buf)) break; } } // Export to single file struct BatchResultTotals { BatchResultTotals (void) { for (int i = 0;i < xe::TESTSTATUSCODE_LAST; i++) countByCode[i] = 0; } int countByCode[xe::TESTSTATUSCODE_LAST]; }; class ResultToSingleXmlLogHandler : public xe::TestLogHandler { public: ResultToSingleXmlLogHandler (xe::xml::Writer& writer, BatchResultTotals& totals) : m_writer (writer) , m_totals (totals) { } void setSessionInfo (const xe::SessionInfo&) { } xe::TestCaseResultPtr startTestCaseResult (const char* casePath) { return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath)); } void testCaseResultUpdated (const xe::TestCaseResultPtr&) { } void testCaseResultComplete (const xe::TestCaseResultPtr& resultData) { xe::TestCaseResult result; xe::parseTestCaseResultFromData(&m_resultParser, &result, *resultData.get()); // Write result. xe::writeTestResult(result, m_writer); // Record total XE_CHECK(de::inBounds(result.statusCode, 0, xe::TESTSTATUSCODE_LAST)); m_totals.countByCode[result.statusCode] += 1; } private: xe::xml::Writer& m_writer; BatchResultTotals& m_totals; xe::TestResultParser m_resultParser; }; static void writeTotals (xe::xml::Writer& writer, const BatchResultTotals& totals) { using xe::xml::Writer; int totalCases = 0; writer << Writer::BeginElement("ResultTotals"); for (int code = 0; code < xe::TESTSTATUSCODE_LAST; code++) { writer << Writer::Attribute(xe::getTestStatusCodeName((xe::TestStatusCode)code), de::toString(totals.countByCode[code]).c_str()); totalCases += totals.countByCode[code]; } writer << Writer::Attribute("All", de::toString(totalCases).c_str()) << Writer::EndElement; } static void batchResultToSingleXmlFile (const char* batchResultFilename, const char* dstFileName) { std::ofstream out (dstFileName, std::ios_base::binary); xe::xml::Writer writer (out); BatchResultTotals totals; ResultToSingleXmlLogHandler handler (writer, totals); xe::TestLogParser parser (&handler); XE_CHECK(out.good()); out << "\n" << "\n"; writer << xe::xml::Writer::BeginElement("BatchResult") << xe::xml::Writer::Attribute("FileName", de::FilePath(batchResultFilename).getBaseName()); // Parse and write individual cases parseBatchResult(parser, batchResultFilename); // Write ResultTotals writeTotals(writer, totals); writer << xe::xml::Writer::EndElement; out << "\n"; } // Export to separate files class ResultToXmlFilesLogHandler : public xe::TestLogHandler { public: ResultToXmlFilesLogHandler (vector& resultHeaders, const char* dstPath) : m_resultHeaders (resultHeaders) , m_dstPath (dstPath) { } void setSessionInfo (const xe::SessionInfo&) { } xe::TestCaseResultPtr startTestCaseResult (const char* casePath) { return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath)); } void testCaseResultUpdated (const xe::TestCaseResultPtr&) { } void testCaseResultComplete (const xe::TestCaseResultPtr& resultData) { xe::TestCaseResult result; xe::parseTestCaseResultFromData(&m_resultParser, &result, *resultData.get()); // Write result. { de::FilePath casePath = de::FilePath::join(m_dstPath, (result.casePath + ".xml").c_str()); std::ofstream out (casePath.getPath(), std::ofstream::binary|std::ofstream::trunc); xe::xml::Writer xmlWriter (out); if (!out.good()) throw xe::Error(string("Failed to open ") + casePath.getPath()); out << "\n" << "\n"; xe::writeTestResult(result, xmlWriter); out << "\n"; } m_resultHeaders.push_back(xe::TestCaseResultHeader(result)); } private: vector& m_resultHeaders; std::string m_dstPath; xe::TestResultParser m_resultParser; }; typedef std::map ShortTestResultMap; static void writeTestCaseListNode (const xe::TestNode* testNode, const ShortTestResultMap& resultMap, xe::xml::Writer& dst) { using xe::xml::Writer; bool isGroup = testNode->getNodeType() == xe::TESTNODETYPE_GROUP; string fullPath; testNode->getFullPath(fullPath); if (isGroup) { const xe::TestGroup* group = static_cast(testNode); dst << Writer::BeginElement("TestGroup") << Writer::Attribute("Name", testNode->getName()); for (int childNdx = 0; childNdx < group->getNumChildren(); childNdx++) writeTestCaseListNode(group->getChild(childNdx), resultMap, dst); dst << Writer::EndElement; } else { DE_ASSERT(testNode->getNodeType() == xe::TESTNODETYPE_TEST_CASE); const xe::TestCase* testCase = static_cast(testNode); ShortTestResultMap::const_iterator resultPos = resultMap.find(testCase); const xe::TestCaseResultHeader* result = resultPos != resultMap.end() ? resultPos->second : DE_NULL; DE_ASSERT(result); dst << Writer::BeginElement("TestCase") << Writer::Attribute("Name", testNode->getName()) << Writer::Attribute("Type", xe::getTestCaseTypeName(result->caseType)) << Writer::Attribute("StatusCode", xe::getTestStatusCodeName(result->statusCode)) << Writer::Attribute("StatusDetails", result->statusDetails.c_str()) << Writer::EndElement; } } static void writeTestCaseList (const xe::TestRoot& root, const ShortTestResultMap& resultMap, xe::xml::Writer& dst) { using xe::xml::Writer; dst << Writer::BeginElement("TestRoot"); for (int childNdx = 0; childNdx < root.getNumChildren(); childNdx++) writeTestCaseListNode(root.getChild(childNdx), resultMap, dst); dst << Writer::EndElement; } static void batchResultToSeparateXmlFiles (const char* batchResultFilename, const char* dstPath) { xe::TestRoot testRoot; vector shortResults; ShortTestResultMap resultMap; // Initialize destination directory. if (!de::FilePath(dstPath).exists()) de::createDirectoryAndParents(dstPath); else XE_CHECK_MSG(de::FilePath(dstPath).getType() == de::FilePath::TYPE_DIRECTORY, "Destination is not directory"); // Parse batch result and write out test cases. { ResultToXmlFilesLogHandler handler (shortResults, dstPath); xe::TestLogParser parser (&handler); parseBatchResult(parser, batchResultFilename); } // Build case hierarchy & short result map. { xe::TestHierarchyBuilder hierarchyBuilder(&testRoot); for (vector::const_iterator result = shortResults.begin(); result != shortResults.end(); result++) { xe::TestCase* testCase = hierarchyBuilder.createCase(result->casePath.c_str(), result->caseType); resultMap.insert(std::make_pair(testCase, &(*result))); } } // Create caselist. { de::FilePath indexPath = de::FilePath::join(dstPath, "caselist.xml"); std::ofstream out (indexPath.getPath(), std::ofstream::binary|std::ofstream::trunc); xe::xml::Writer xmlWriter (out); XE_CHECK_MSG(out.good(), "Failed to open caselist.xml"); out << "\n" << "\n"; writeTestCaseList(testRoot, resultMap, xmlWriter); out << "\n"; } } int main (int argc, const char* const* argv) { try { CommandLine cmdLine; if (!parseCommandLine(cmdLine, argc, argv)) return -1; if (cmdLine.outputMode == OUTPUTMODE_SINGLE) batchResultToSingleXmlFile(cmdLine.batchResultFile.c_str(), cmdLine.outputPath.c_str()); else batchResultToSeparateXmlFiles(cmdLine.batchResultFile.c_str(), cmdLine.outputPath.c_str()); } catch (const std::exception& e) { printf("%s\n", e.what()); return -1; } return 0; }