mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-26 07:16:10 +01:00
Move SonarQube reporter implementation to a cpp file
This commit is contained in:
parent
e6ea53ab49
commit
0b2874b6b1
@ -5,8 +5,131 @@
|
|||||||
|
|
||||||
#include <catch2/reporters/catch_reporter_sonarqube.hpp>
|
#include <catch2/reporters/catch_reporter_sonarqube.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
SonarQubeReporter::~SonarQubeReporter() {}
|
SonarQubeReporter::~SonarQubeReporter() {}
|
||||||
|
|
||||||
|
void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) {
|
||||||
|
CumulativeReporterBase::testRunStarting(testRunInfo);
|
||||||
|
xml.startElement("testExecutions");
|
||||||
|
xml.writeAttribute("version", '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::testGroupEnded(TestGroupStats const& testGroupStats) {
|
||||||
|
CumulativeReporterBase::testGroupEnded(testGroupStats);
|
||||||
|
writeGroup(*m_testGroups.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::writeGroup(TestGroupNode const& groupNode) {
|
||||||
|
std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
|
||||||
|
for (auto const& child : groupNode.children)
|
||||||
|
testsPerFile[child->value.testInfo->lineInfo.file].push_back(child);
|
||||||
|
|
||||||
|
for (auto const& kv : testsPerFile)
|
||||||
|
writeTestFile(kv.first, kv.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::writeTestFile(std::string const& filename, TestGroupNode::ChildNodes const& testCaseNodes) {
|
||||||
|
XmlWriter::ScopedElement e = xml.scopedElement("file");
|
||||||
|
xml.writeAttribute("path", filename);
|
||||||
|
|
||||||
|
for (auto const& child : testCaseNodes)
|
||||||
|
writeTestCase(*child);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::writeTestCase(TestCaseNode const& testCaseNode) {
|
||||||
|
// All test cases have exactly one section - which represents the
|
||||||
|
// test case itself. That section may have 0-n nested sections
|
||||||
|
assert(testCaseNode.children.size() == 1);
|
||||||
|
SectionNode const& rootSection = *testCaseNode.children.front();
|
||||||
|
writeSection("", rootSection, testCaseNode.value.testInfo->okToFail());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
|
||||||
|
std::string name = trim(sectionNode.stats.sectionInfo.name);
|
||||||
|
if (!rootName.empty())
|
||||||
|
name = rootName + '/' + name;
|
||||||
|
|
||||||
|
if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
|
||||||
|
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
|
||||||
|
xml.writeAttribute("name", name);
|
||||||
|
xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
|
||||||
|
|
||||||
|
writeAssertions(sectionNode, okToFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& childNode : sectionNode.childSections)
|
||||||
|
writeSection(name, *childNode, okToFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::writeAssertions(SectionNode const& sectionNode, bool okToFail) {
|
||||||
|
for (auto const& assertion : sectionNode.assertions)
|
||||||
|
writeAssertion(assertion, okToFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) {
|
||||||
|
AssertionResult const& result = stats.assertionResult;
|
||||||
|
if (!result.isOk()) {
|
||||||
|
std::string elementName;
|
||||||
|
if (okToFail) {
|
||||||
|
elementName = "skipped";
|
||||||
|
} else {
|
||||||
|
switch (result.getResultType()) {
|
||||||
|
case ResultWas::ThrewException:
|
||||||
|
case ResultWas::FatalErrorCondition:
|
||||||
|
elementName = "error";
|
||||||
|
break;
|
||||||
|
case ResultWas::ExplicitFailure:
|
||||||
|
elementName = "failure";
|
||||||
|
break;
|
||||||
|
case ResultWas::ExpressionFailed:
|
||||||
|
elementName = "failure";
|
||||||
|
break;
|
||||||
|
case ResultWas::DidntThrowException:
|
||||||
|
elementName = "failure";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// We should never see these here:
|
||||||
|
case ResultWas::Info:
|
||||||
|
case ResultWas::Warning:
|
||||||
|
case ResultWas::Ok:
|
||||||
|
case ResultWas::Unknown:
|
||||||
|
case ResultWas::FailureBit:
|
||||||
|
case ResultWas::Exception:
|
||||||
|
elementName = "internalError";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
|
||||||
|
|
||||||
|
ReusableStringStream messageRss;
|
||||||
|
messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
|
||||||
|
xml.writeAttribute("message", messageRss.str());
|
||||||
|
|
||||||
|
ReusableStringStream textRss;
|
||||||
|
if (stats.totals.assertions.total() > 0) {
|
||||||
|
textRss << "FAILED:\n";
|
||||||
|
if (result.hasExpression()) {
|
||||||
|
textRss << "\t" << result.getExpressionInMacro() << "\n";
|
||||||
|
}
|
||||||
|
if (result.hasExpandedExpression()) {
|
||||||
|
textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.getMessage().empty())
|
||||||
|
textRss << result.getMessage() << "\n";
|
||||||
|
|
||||||
|
for (auto const& msg : stats.infoMessages)
|
||||||
|
if (msg.type == ResultWas::Info)
|
||||||
|
textRss << msg.message << "\n";
|
||||||
|
|
||||||
|
textRss << "at " << result.getSourceInfo();
|
||||||
|
xml.writeText(textRss.str(), XmlFormatting::Newline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace Catch
|
} // end namespace Catch
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
/*
|
/*
|
||||||
* Created by Daniel Garcia on 2018-12-04.
|
|
||||||
* Copyright Social Point SL. All rights reserved.
|
|
||||||
*
|
|
||||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
*/
|
*/
|
||||||
#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||||
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||||
|
|
||||||
#include <catch2/catch_xmlwriter.h>
|
|
||||||
#include <catch2/reporters/catch_reporter_bases.hpp>
|
#include <catch2/reporters/catch_reporter_bases.hpp>
|
||||||
|
|
||||||
#include <map>
|
#include <catch2/catch_xmlwriter.h>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
@ -27,140 +23,31 @@ namespace Catch {
|
|||||||
~SonarQubeReporter() override;
|
~SonarQubeReporter() override;
|
||||||
|
|
||||||
static std::string getDescription() {
|
static std::string getDescription() {
|
||||||
return "Reports test results in the Generic Test Data SonarQube XML format";
|
using namespace std::string_literals;
|
||||||
}
|
return "Reports test results in the Generic Test Data SonarQube XML format"s;
|
||||||
|
|
||||||
static std::set<Verbosity> getSupportedVerbosities() {
|
|
||||||
return { Verbosity::Normal };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void noMatchingTestCases(std::string const& /*spec*/) override {}
|
void noMatchingTestCases(std::string const& /*spec*/) override {}
|
||||||
|
|
||||||
void testRunStarting(TestRunInfo const& testRunInfo) override {
|
void testRunStarting(TestRunInfo const& testRunInfo) override;
|
||||||
CumulativeReporterBase::testRunStarting(testRunInfo);
|
|
||||||
xml.startElement("testExecutions");
|
|
||||||
xml.writeAttribute("version", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
void testGroupEnded(TestGroupStats const& testGroupStats) override {
|
void testGroupEnded(TestGroupStats const& testGroupStats) override;
|
||||||
CumulativeReporterBase::testGroupEnded(testGroupStats);
|
|
||||||
writeGroup(*m_testGroups.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
void testRunEndedCumulative() override {
|
void testRunEndedCumulative() override {
|
||||||
xml.endElement();
|
xml.endElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeGroup(TestGroupNode const& groupNode) {
|
void writeGroup(TestGroupNode const& groupNode);
|
||||||
std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
|
|
||||||
for(auto const& child : groupNode.children)
|
|
||||||
testsPerFile[child->value.testInfo->lineInfo.file].push_back(child);
|
|
||||||
|
|
||||||
for(auto const& kv : testsPerFile)
|
void writeTestFile(std::string const& filename, TestGroupNode::ChildNodes const& testCaseNodes);
|
||||||
writeTestFile(kv.first.c_str(), kv.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
|
void writeTestCase(TestCaseNode const& testCaseNode);
|
||||||
XmlWriter::ScopedElement e = xml.scopedElement("file");
|
|
||||||
xml.writeAttribute("path", filename);
|
|
||||||
|
|
||||||
for(auto const& child : testCaseNodes)
|
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail);
|
||||||
writeTestCase(*child);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeTestCase(TestCaseNode const& testCaseNode) {
|
void writeAssertions(SectionNode const& sectionNode, bool okToFail);
|
||||||
// All test cases have exactly one section - which represents the
|
|
||||||
// test case itself. That section may have 0-n nested sections
|
|
||||||
assert(testCaseNode.children.size() == 1);
|
|
||||||
SectionNode const& rootSection = *testCaseNode.children.front();
|
|
||||||
writeSection("", rootSection, testCaseNode.value.testInfo->okToFail());
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
|
void writeAssertion(AssertionStats const& stats, bool okToFail);
|
||||||
std::string name = trim(sectionNode.stats.sectionInfo.name);
|
|
||||||
if(!rootName.empty())
|
|
||||||
name = rootName + '/' + name;
|
|
||||||
|
|
||||||
if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
|
|
||||||
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
|
|
||||||
xml.writeAttribute("name", name);
|
|
||||||
xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
|
|
||||||
|
|
||||||
writeAssertions(sectionNode, okToFail);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto const& childNode : sectionNode.childSections)
|
|
||||||
writeSection(name, *childNode, okToFail);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeAssertions(SectionNode const& sectionNode, bool okToFail) {
|
|
||||||
for(auto const& assertion : sectionNode.assertions)
|
|
||||||
writeAssertion( assertion, okToFail);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeAssertion(AssertionStats const& stats, bool okToFail) {
|
|
||||||
AssertionResult const& result = stats.assertionResult;
|
|
||||||
if(!result.isOk()) {
|
|
||||||
std::string elementName;
|
|
||||||
if(okToFail) {
|
|
||||||
elementName = "skipped";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch(result.getResultType()) {
|
|
||||||
case ResultWas::ThrewException:
|
|
||||||
case ResultWas::FatalErrorCondition:
|
|
||||||
elementName = "error";
|
|
||||||
break;
|
|
||||||
case ResultWas::ExplicitFailure:
|
|
||||||
elementName = "failure";
|
|
||||||
break;
|
|
||||||
case ResultWas::ExpressionFailed:
|
|
||||||
elementName = "failure";
|
|
||||||
break;
|
|
||||||
case ResultWas::DidntThrowException:
|
|
||||||
elementName = "failure";
|
|
||||||
break;
|
|
||||||
|
|
||||||
// We should never see these here:
|
|
||||||
case ResultWas::Info:
|
|
||||||
case ResultWas::Warning:
|
|
||||||
case ResultWas::Ok:
|
|
||||||
case ResultWas::Unknown:
|
|
||||||
case ResultWas::FailureBit:
|
|
||||||
case ResultWas::Exception:
|
|
||||||
elementName = "internalError";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
|
|
||||||
|
|
||||||
ReusableStringStream messageRss;
|
|
||||||
messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
|
|
||||||
xml.writeAttribute("message", messageRss.str());
|
|
||||||
|
|
||||||
ReusableStringStream textRss;
|
|
||||||
if (stats.totals.assertions.total() > 0) {
|
|
||||||
textRss << "FAILED:\n";
|
|
||||||
if (result.hasExpression()) {
|
|
||||||
textRss << "\t" << result.getExpressionInMacro() << "\n";
|
|
||||||
}
|
|
||||||
if (result.hasExpandedExpression()) {
|
|
||||||
textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!result.getMessage().empty())
|
|
||||||
textRss << result.getMessage() << "\n";
|
|
||||||
|
|
||||||
for(auto const& msg : stats.infoMessages)
|
|
||||||
if(msg.type == ResultWas::Info)
|
|
||||||
textRss << msg.message << "\n";
|
|
||||||
|
|
||||||
textRss << "at " << result.getSourceInfo();
|
|
||||||
xml.writeText(textRss.str(), XmlFormatting::Newline);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XmlWriter xml;
|
XmlWriter xml;
|
||||||
|
Loading…
Reference in New Issue
Block a user