blob: 3f5b03a81bf6a919c1f676cc8652f8c0c0c128cf [file] [log] [blame] [edit]
/*-------------------------------------------------------------------------
* 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 <vector>
#include <string>
#include <map>
#include <cstdio>
#include <fstream>
#include <iostream>
using std::map;
using std::string;
using std::vector;
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::NamedValue;
using de::cmdline::Option;
static const NamedValue<OutputMode> s_modes[] = {{"single", OUTPUTMODE_SINGLE}, {"separate", OUTPUTMODE_SEPARATE}};
parser << Option<OutMode>("m", "mode", "Output mode", s_modes, "single");
}
} // namespace 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<opt::OutMode>();
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);
uint8_t 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<int>(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 << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
<< "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\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<xe::TestCaseResultHeader> &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 << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
<< "<?xml-stylesheet href=\"" << TESTCASE_STYLESHEET << "\" type=\"text/xsl\"?>\n";
xe::writeTestResult(result, xmlWriter);
out << "\n";
}
m_resultHeaders.push_back(xe::TestCaseResultHeader(result));
}
private:
vector<xe::TestCaseResultHeader> &m_resultHeaders;
std::string m_dstPath;
xe::TestResultParser m_resultParser;
};
typedef std::map<const xe::TestCase *, const xe::TestCaseResultHeader *> 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<const xe::TestGroup *>(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<const xe::TestCase *>(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<xe::TestCaseResultHeader> 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<xe::TestCaseResultHeader>::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 << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
<< "<?xml-stylesheet href=\"" << CASELIST_STYLESHEET << "\" type=\"text/xsl\"?>\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;
}