catch_reporter_sonarqube.hpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Created by Daniel Garcia on 2018-12-04.
  3. * Copyright Social Point SL. All rights reserved.
  4. *
  5. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  6. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. */
  8. #ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
  9. #define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
  10. // Don't #include any Catch headers here - we can assume they are already
  11. // included before this header.
  12. // This is not good practice in general but is necessary in this case so this
  13. // file can be distributed as a single header that works with the main
  14. // Catch single header.
  15. #include <map>
  16. namespace Catch {
  17. struct SonarQubeReporter : CumulativeReporterBase<SonarQubeReporter> {
  18. SonarQubeReporter(ReporterConfig const& config)
  19. : CumulativeReporterBase(config)
  20. , xml(config.stream()) {
  21. m_reporterPrefs.shouldRedirectStdOut = true;
  22. m_reporterPrefs.shouldReportAllAssertions = true;
  23. }
  24. ~SonarQubeReporter() override;
  25. static std::string getDescription() {
  26. return "Reports test results in the Generic Test Data SonarQube XML format";
  27. }
  28. static std::set<Verbosity> getSupportedVerbosities() {
  29. return { Verbosity::Normal };
  30. }
  31. void noMatchingTestCases(std::string const& /*spec*/) override {}
  32. void testRunStarting(TestRunInfo const& testRunInfo) override {
  33. CumulativeReporterBase::testRunStarting(testRunInfo);
  34. xml.startElement("testExecutions");
  35. xml.writeAttribute("version", "1");
  36. }
  37. void testGroupEnded(TestGroupStats const& testGroupStats) override {
  38. CumulativeReporterBase::testGroupEnded(testGroupStats);
  39. writeGroup(*m_testGroups.back());
  40. }
  41. void testRunEndedCumulative() override {
  42. xml.endElement();
  43. }
  44. void writeGroup(TestGroupNode const& groupNode) {
  45. std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
  46. for(auto const& child : groupNode.children)
  47. testsPerFile[child->value.testInfo.lineInfo.file].push_back(child);
  48. for(auto const& kv : testsPerFile)
  49. writeTestFile(kv.first.c_str(), kv.second);
  50. }
  51. void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
  52. XmlWriter::ScopedElement e = xml.scopedElement("file");
  53. xml.writeAttribute("path", filename);
  54. for(auto const& child : testCaseNodes)
  55. writeTestCase(*child);
  56. }
  57. void writeTestCase(TestCaseNode const& testCaseNode) {
  58. // All test cases have exactly one section - which represents the
  59. // test case itself. That section may have 0-n nested sections
  60. assert(testCaseNode.children.size() == 1);
  61. SectionNode const& rootSection = *testCaseNode.children.front();
  62. writeSection("", rootSection, testCaseNode.value.testInfo.okToFail());
  63. }
  64. void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
  65. std::string name = trim(sectionNode.stats.sectionInfo.name);
  66. if(!rootName.empty())
  67. name = rootName + '/' + name;
  68. if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
  69. XmlWriter::ScopedElement e = xml.scopedElement("testCase");
  70. xml.writeAttribute("name", name);
  71. xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
  72. writeAssertions(sectionNode, okToFail);
  73. }
  74. for(auto const& childNode : sectionNode.childSections)
  75. writeSection(name, *childNode, okToFail);
  76. }
  77. void writeAssertions(SectionNode const& sectionNode, bool okToFail) {
  78. for(auto const& assertion : sectionNode.assertions)
  79. writeAssertion( assertion, okToFail);
  80. }
  81. void writeAssertion(AssertionStats const& stats, bool okToFail) {
  82. AssertionResult const& result = stats.assertionResult;
  83. if(!result.isOk()) {
  84. std::string elementName;
  85. if(okToFail) {
  86. elementName = "skipped";
  87. }
  88. else {
  89. switch(result.getResultType()) {
  90. case ResultWas::ThrewException:
  91. case ResultWas::FatalErrorCondition:
  92. elementName = "error";
  93. break;
  94. case ResultWas::ExplicitFailure:
  95. elementName = "failure";
  96. break;
  97. case ResultWas::ExpressionFailed:
  98. elementName = "failure";
  99. break;
  100. case ResultWas::DidntThrowException:
  101. elementName = "failure";
  102. break;
  103. // We should never see these here:
  104. case ResultWas::Info:
  105. case ResultWas::Warning:
  106. case ResultWas::Ok:
  107. case ResultWas::Unknown:
  108. case ResultWas::FailureBit:
  109. case ResultWas::Exception:
  110. elementName = "internalError";
  111. break;
  112. }
  113. }
  114. XmlWriter::ScopedElement e = xml.scopedElement(elementName);
  115. ReusableStringStream messageRss;
  116. messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
  117. xml.writeAttribute("message", messageRss.str());
  118. ReusableStringStream textRss;
  119. if (stats.totals.assertions.total() > 0) {
  120. textRss << "FAILED:\n";
  121. if (result.hasExpression()) {
  122. textRss << "\t" << result.getExpressionInMacro() << "\n";
  123. }
  124. if (result.hasExpandedExpression()) {
  125. textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
  126. }
  127. }
  128. if(!result.getMessage().empty())
  129. textRss << result.getMessage() << "\n";
  130. for(auto const& msg : stats.infoMessages)
  131. if(msg.type == ResultWas::Info)
  132. textRss << msg.message << "\n";
  133. textRss << "at " << result.getSourceInfo();
  134. xml.writeText(textRss.str(), XmlFormatting::Newline);
  135. }
  136. }
  137. private:
  138. XmlWriter xml;
  139. };
  140. #ifdef CATCH_IMPL
  141. SonarQubeReporter::~SonarQubeReporter() {}
  142. #endif
  143. CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter )
  144. } // end namespace Catch
  145. #endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED