/ externals / catch / src / catch2 / reporters / catch_reporter_xml.cpp
catch_reporter_xml.cpp
  1  
  2  //              Copyright Catch2 Authors
  3  // Distributed under the Boost Software License, Version 1.0.
  4  //   (See accompanying file LICENSE.txt or copy at
  5  //        https://www.boost.org/LICENSE_1_0.txt)
  6  
  7  // SPDX-License-Identifier: BSL-1.0
  8  #include <catch2/reporters/catch_reporter_xml.hpp>
  9  
 10  #include <catch2/reporters/catch_reporter_helpers.hpp>
 11  #include <catch2/interfaces/catch_interfaces_config.hpp>
 12  #include <catch2/catch_test_spec.hpp>
 13  #include <catch2/internal/catch_string_manip.hpp>
 14  #include <catch2/internal/catch_list.hpp>
 15  #include <catch2/catch_test_case_info.hpp>
 16  #include <catch2/internal/catch_move_and_forward.hpp>
 17  #include <catch2/catch_version.hpp>
 18  
 19  #if defined(_MSC_VER)
 20  #pragma warning(push)
 21  #pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
 22                                // Note that 4062 (not all labels are handled
 23                                // and default is missing) is enabled
 24  #endif
 25  
 26  namespace Catch {
 27      XmlReporter::XmlReporter( ReporterConfig&& _config )
 28      :   StreamingReporterBase( CATCH_MOVE(_config) ),
 29          m_xml(m_stream)
 30      {
 31          m_preferences.shouldRedirectStdOut = true;
 32          m_preferences.shouldReportAllAssertions = true;
 33      }
 34  
 35      XmlReporter::~XmlReporter() = default;
 36  
 37      std::string XmlReporter::getDescription() {
 38          return "Reports test results as an XML document";
 39      }
 40  
 41      std::string XmlReporter::getStylesheetRef() const {
 42          return std::string();
 43      }
 44  
 45      void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
 46          m_xml
 47              .writeAttribute( "filename"_sr, sourceInfo.file )
 48              .writeAttribute( "line"_sr, sourceInfo.line );
 49      }
 50  
 51      void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
 52          StreamingReporterBase::testRunStarting( testInfo );
 53          std::string stylesheetRef = getStylesheetRef();
 54          if( !stylesheetRef.empty() )
 55              m_xml.writeStylesheetRef( stylesheetRef );
 56          m_xml.startElement("Catch2TestRun")
 57               .writeAttribute("name"_sr, m_config->name())
 58               .writeAttribute("rng-seed"_sr, m_config->rngSeed())
 59               .writeAttribute("xml-format-version"_sr, 3)
 60               .writeAttribute("catch2-version"_sr, libraryVersion());
 61          if ( m_config->testSpec().hasFilters() ) {
 62              m_xml.writeAttribute( "filters"_sr, m_config->testSpec() );
 63          }
 64      }
 65  
 66      void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
 67          StreamingReporterBase::testCaseStarting(testInfo);
 68          m_xml.startElement( "TestCase" )
 69              .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) )
 70              .writeAttribute( "tags"_sr, testInfo.tagsAsString() );
 71  
 72          writeSourceInfo( testInfo.lineInfo );
 73  
 74          if ( m_config->showDurations() == ShowDurations::Always )
 75              m_testCaseTimer.start();
 76          m_xml.ensureTagClosed();
 77      }
 78  
 79      void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) {
 80          StreamingReporterBase::sectionStarting( sectionInfo );
 81          if( m_sectionDepth++ > 0 ) {
 82              m_xml.startElement( "Section" )
 83                  .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) );
 84              writeSourceInfo( sectionInfo.lineInfo );
 85              m_xml.ensureTagClosed();
 86          }
 87      }
 88  
 89      void XmlReporter::assertionStarting( AssertionInfo const& ) { }
 90  
 91      void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
 92  
 93          AssertionResult const& result = assertionStats.assertionResult;
 94  
 95          bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
 96  
 97          if( includeResults || result.getResultType() == ResultWas::Warning ) {
 98              // Print any info messages in <Info> tags.
 99              for( auto const& msg : assertionStats.infoMessages ) {
100                  if( msg.type == ResultWas::Info && includeResults ) {
101                      auto t = m_xml.scopedElement( "Info" );
102                      writeSourceInfo( msg.lineInfo );
103                      t.writeText( msg.message );
104                  } else if ( msg.type == ResultWas::Warning ) {
105                      auto t = m_xml.scopedElement( "Warning" );
106                      writeSourceInfo( msg.lineInfo );
107                      t.writeText( msg.message );
108                  }
109              }
110          }
111  
112          // Drop out if result was successful but we're not printing them.
113          if ( !includeResults && result.getResultType() != ResultWas::Warning &&
114               result.getResultType() != ResultWas::ExplicitSkip ) {
115              return;
116          }
117  
118          // Print the expression if there is one.
119          if( result.hasExpression() ) {
120              m_xml.startElement( "Expression" )
121                  .writeAttribute( "success"_sr, result.succeeded() )
122                  .writeAttribute( "type"_sr, result.getTestMacroName() );
123  
124              writeSourceInfo( result.getSourceInfo() );
125  
126              m_xml.scopedElement( "Original" )
127                  .writeText( result.getExpression() );
128              m_xml.scopedElement( "Expanded" )
129                  .writeText( result.getExpandedExpression() );
130          }
131  
132          // And... Print a result applicable to each result type.
133          switch( result.getResultType() ) {
134              case ResultWas::ThrewException:
135                  m_xml.startElement( "Exception" );
136                  writeSourceInfo( result.getSourceInfo() );
137                  m_xml.writeText( result.getMessage() );
138                  m_xml.endElement();
139                  break;
140              case ResultWas::FatalErrorCondition:
141                  m_xml.startElement( "FatalErrorCondition" );
142                  writeSourceInfo( result.getSourceInfo() );
143                  m_xml.writeText( result.getMessage() );
144                  m_xml.endElement();
145                  break;
146              case ResultWas::Info:
147                  m_xml.scopedElement( "Info" )
148                       .writeText( result.getMessage() );
149                  break;
150              case ResultWas::Warning:
151                  // Warning will already have been written
152                  break;
153              case ResultWas::ExplicitFailure:
154                  m_xml.startElement( "Failure" );
155                  writeSourceInfo( result.getSourceInfo() );
156                  m_xml.writeText( result.getMessage() );
157                  m_xml.endElement();
158                  break;
159              case ResultWas::ExplicitSkip:
160                  m_xml.startElement( "Skip" );
161                  writeSourceInfo( result.getSourceInfo() );
162                  m_xml.writeText( result.getMessage() );
163                  m_xml.endElement();
164                  break;
165              default:
166                  break;
167          }
168  
169          if( result.hasExpression() )
170              m_xml.endElement();
171      }
172  
173      void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
174          StreamingReporterBase::sectionEnded( sectionStats );
175          if ( --m_sectionDepth > 0 ) {
176              {
177                  XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
178                  e.writeAttribute( "successes"_sr, sectionStats.assertions.passed );
179                  e.writeAttribute( "failures"_sr, sectionStats.assertions.failed );
180                  e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk );
181                  e.writeAttribute( "skipped"_sr, sectionStats.assertions.skipped > 0 );
182  
183                  if ( m_config->showDurations() == ShowDurations::Always )
184                      e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds );
185              }
186              // Ends assertion tag
187              m_xml.endElement();
188          }
189      }
190  
191      void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
192          StreamingReporterBase::testCaseEnded( testCaseStats );
193          XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
194          e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() );
195          e.writeAttribute( "skips"_sr, testCaseStats.totals.assertions.skipped );
196  
197          if ( m_config->showDurations() == ShowDurations::Always )
198              e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
199          if( !testCaseStats.stdOut.empty() )
200              m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline );
201          if( !testCaseStats.stdErr.empty() )
202              m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline );
203  
204          m_xml.endElement();
205      }
206  
207      void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
208          StreamingReporterBase::testRunEnded( testRunStats );
209          m_xml.scopedElement( "OverallResults" )
210              .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed )
211              .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed )
212              .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk )
213              .writeAttribute( "skips"_sr, testRunStats.totals.assertions.skipped );
214          m_xml.scopedElement( "OverallResultsCases")
215              .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed )
216              .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed )
217              .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk )
218              .writeAttribute( "skips"_sr, testRunStats.totals.testCases.skipped );
219          m_xml.endElement();
220      }
221  
222      void XmlReporter::benchmarkPreparing( StringRef name ) {
223          m_xml.startElement("BenchmarkResults")
224               .writeAttribute("name"_sr, name);
225      }
226  
227      void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
228          m_xml.writeAttribute("samples"_sr, info.samples)
229              .writeAttribute("resamples"_sr, info.resamples)
230              .writeAttribute("iterations"_sr, info.iterations)
231              .writeAttribute("clockResolution"_sr, info.clockResolution)
232              .writeAttribute("estimatedDuration"_sr, info.estimatedDuration)
233              .writeComment("All values in nano seconds"_sr);
234      }
235  
236      void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
237          m_xml.scopedElement("mean")
238              .writeAttribute("value"_sr, benchmarkStats.mean.point.count())
239              .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count())
240              .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count())
241              .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval);
242          m_xml.scopedElement("standardDeviation")
243              .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count())
244              .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count())
245              .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count())
246              .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval);
247          m_xml.scopedElement("outliers")
248              .writeAttribute("variance"_sr, benchmarkStats.outlierVariance)
249              .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild)
250              .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe)
251              .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild)
252              .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe);
253          m_xml.endElement();
254      }
255  
256      void XmlReporter::benchmarkFailed(StringRef error) {
257          m_xml.scopedElement("failed").
258              writeAttribute("message"_sr, error);
259          m_xml.endElement();
260      }
261  
262      void XmlReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
263          auto outerTag = m_xml.scopedElement("AvailableReporters");
264          for (auto const& reporter : descriptions) {
265              auto inner = m_xml.scopedElement("Reporter");
266              m_xml.startElement("Name", XmlFormatting::Indent)
267                   .writeText(reporter.name, XmlFormatting::None)
268                   .endElement(XmlFormatting::Newline);
269              m_xml.startElement("Description", XmlFormatting::Indent)
270                   .writeText(reporter.description, XmlFormatting::None)
271                   .endElement(XmlFormatting::Newline);
272          }
273      }
274  
275      void XmlReporter::listListeners(std::vector<ListenerDescription> const& descriptions) {
276          auto outerTag = m_xml.scopedElement( "RegisteredListeners" );
277          for ( auto const& listener : descriptions ) {
278              auto inner = m_xml.scopedElement( "Listener" );
279              m_xml.startElement( "Name", XmlFormatting::Indent )
280                  .writeText( listener.name, XmlFormatting::None )
281                  .endElement( XmlFormatting::Newline );
282              m_xml.startElement( "Description", XmlFormatting::Indent )
283                  .writeText( listener.description, XmlFormatting::None )
284                  .endElement( XmlFormatting::Newline );
285          }
286      }
287  
288      void XmlReporter::listTests(std::vector<TestCaseHandle> const& tests) {
289          auto outerTag = m_xml.scopedElement("MatchingTests");
290          for (auto const& test : tests) {
291              auto innerTag = m_xml.scopedElement("TestCase");
292              auto const& testInfo = test.getTestCaseInfo();
293              m_xml.startElement("Name", XmlFormatting::Indent)
294                   .writeText(testInfo.name, XmlFormatting::None)
295                   .endElement(XmlFormatting::Newline);
296              m_xml.startElement("ClassName", XmlFormatting::Indent)
297                   .writeText(testInfo.className, XmlFormatting::None)
298                   .endElement(XmlFormatting::Newline);
299              m_xml.startElement("Tags", XmlFormatting::Indent)
300                   .writeText(testInfo.tagsAsString(), XmlFormatting::None)
301                   .endElement(XmlFormatting::Newline);
302  
303              auto sourceTag = m_xml.scopedElement("SourceInfo");
304              m_xml.startElement("File", XmlFormatting::Indent)
305                   .writeText(testInfo.lineInfo.file, XmlFormatting::None)
306                   .endElement(XmlFormatting::Newline);
307              m_xml.startElement("Line", XmlFormatting::Indent)
308                   .writeText(std::to_string(testInfo.lineInfo.line), XmlFormatting::None)
309                   .endElement(XmlFormatting::Newline);
310          }
311      }
312  
313      void XmlReporter::listTags(std::vector<TagInfo> const& tags) {
314          auto outerTag = m_xml.scopedElement("TagsFromMatchingTests");
315          for (auto const& tag : tags) {
316              auto innerTag = m_xml.scopedElement("Tag");
317              m_xml.startElement("Count", XmlFormatting::Indent)
318                   .writeText(std::to_string(tag.count), XmlFormatting::None)
319                   .endElement(XmlFormatting::Newline);
320              auto aliasTag = m_xml.scopedElement("Aliases");
321              for (auto const& alias : tag.spellings) {
322                  m_xml.startElement("Alias", XmlFormatting::Indent)
323                       .writeText(alias, XmlFormatting::None)
324                       .endElement(XmlFormatting::Newline);
325              }
326          }
327      }
328  
329  } // end namespace Catch
330  
331  #if defined(_MSC_VER)
332  #pragma warning(pop)
333  #endif