You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
401 lines
11 KiB
401 lines
11 KiB
/*-------------------------------------------------------------------------
|
|
* 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::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<OutputMode> s_modes[] =
|
|
{
|
|
{ "single", OUTPUTMODE_SINGLE },
|
|
{ "separate", OUTPUTMODE_SEPARATE }
|
|
};
|
|
|
|
parser << Option<OutMode>("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<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);
|
|
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<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;
|
|
}
|