mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 12:17:11 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			181 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  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
 | |
|  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
|  */
 | |
| #ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
 | |
| #define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
 | |
| 
 | |
| 
 | |
| // Don't #include any Catch headers here - we can assume they are already
 | |
| // included before this header.
 | |
| // This is not good practice in general but is necessary in this case so this
 | |
| // file can be distributed as a single header that works with the main
 | |
| // Catch single header.
 | |
| 
 | |
| #include <map>
 | |
| 
 | |
| namespace Catch {
 | |
| 
 | |
|     struct SonarQubeReporter : CumulativeReporterBase<SonarQubeReporter> {
 | |
| 
 | |
|         SonarQubeReporter(ReporterConfig const& config)
 | |
|         : CumulativeReporterBase(config)
 | |
|         , xml(config.stream()) {
 | |
|             m_reporterPrefs.shouldRedirectStdOut = true;
 | |
|             m_reporterPrefs.shouldReportAllAssertions = true;
 | |
|         }
 | |
| 
 | |
|         ~SonarQubeReporter() override;
 | |
| 
 | |
|         static std::string getDescription() {
 | |
|             return "Reports test results in the Generic Test Data SonarQube XML format";
 | |
|         }
 | |
| 
 | |
|         static std::set<Verbosity> getSupportedVerbosities() {
 | |
|             return { Verbosity::Normal };
 | |
|         }
 | |
| 
 | |
|         void noMatchingTestCases(std::string const& /*spec*/) override {}
 | |
| 
 | |
|         void testRunStarting(TestRunInfo const& testRunInfo) override {
 | |
|             CumulativeReporterBase::testRunStarting(testRunInfo);
 | |
|             xml.startElement("testExecutions");
 | |
|             xml.writeAttribute("version", "1");
 | |
|         }
 | |
| 
 | |
|         void testGroupEnded(TestGroupStats const& testGroupStats) override {
 | |
|             CumulativeReporterBase::testGroupEnded(testGroupStats);
 | |
|             writeGroup(*m_testGroups.back());
 | |
|         }
 | |
| 
 | |
|         void testRunEndedCumulative() override {
 | |
|             xml.endElement();
 | |
|         }
 | |
| 
 | |
|         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)
 | |
|                 writeTestFile(kv.first.c_str(), kv.second);
 | |
|         }
 | |
| 
 | |
|         void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
 | |
|             XmlWriter::ScopedElement e = xml.scopedElement("file");
 | |
|             xml.writeAttribute("path", filename);
 | |
| 
 | |
|             for(auto const& child : testCaseNodes)
 | |
|                 writeTestCase(*child);
 | |
|         }
 | |
| 
 | |
|         void 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 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 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:
 | |
|         XmlWriter xml;
 | |
|     };
 | |
| 
 | |
| #ifdef CATCH_IMPL
 | |
|     SonarQubeReporter::~SonarQubeReporter() {}
 | |
| #endif
 | |
| 
 | |
|     CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter )
 | |
| 
 | |
| } // end namespace Catch
 | |
| 
 | |
| #endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
 | 
