ros_junit_reporter.h
Go to the documentation of this file.
1 // Customized JUnit XML reporter for use with the catkin testing framework
2 // Author: Max Schwarz <max.schwarz@uni-bonn.de>
3 
4 // Based on the JUnit reporter from the Catch framework, written by
5 // Phil Nash. The original source is licensed under the Boost software license.
6 
7 #ifndef CATCH_ROS_ROS_JUNIT_REPORTER_H
8 #define CATCH_ROS_ROS_JUNIT_REPORTER_H
9 
10 
11 // Due to the single-header structure of Catch, this file has to be included
12 // in the main source file defining CATCH_CONFIG_MAIN/RUNNER.
13 // If you like auto-completion/static code checking/whatever, just define
14 // __IDE__ to include the internal Catch stuff here.
15 #ifdef __IDE__
16 # define CATCH_IMPL 1
17 #endif
18 #include <catch_ros/catch.hpp>
19 
20 #include <assert.h>
21 
22 #include "meta_info.h"
23 
24 namespace catch_ros
25 {
26 
27 class ROSReporter : public Catch::CumulativeReporterBase<ROSReporter> {
28 public:
29  ROSReporter( Catch::ReporterConfig const& _config )
30  : CumulativeReporterBase( _config )
31  , xml( _config.stream() )
32  {
33  Catch::ReporterConfig consoleConfig(_config.fullConfig(), consoleOut);
34  console = new Catch::ConsoleReporter(consoleConfig);
35  }
36 
38  {
39  std::cerr << consoleOut.str() << std::flush;
40  delete console;
41  }
42 
43  static std::string getDescription()
44  {
45  return "Reports test result in JUnit format tweaked for ROS";
46  }
47 
48  virtual void noMatchingTestCases( std::string const& spec )
49  {
50  console->noMatchingTestCases(spec);
51  }
52 
53  virtual Catch::ReporterPreferences getPreferences() const
54  {
55  Catch::ReporterPreferences prefs;
56  prefs.shouldRedirectStdOut = true;
57  return prefs;
58  }
59 
60  virtual void testRunStarting( Catch::TestRunInfo const& runInfo )
61  {
62  CumulativeReporterBase::testRunStarting( runInfo );
63  console->testRunStarting(runInfo);
65  }
66 
67  virtual void testRunEnded( Catch::TestRunStats const& testRunStats )
68  {
69  CumulativeReporterBase::testRunEnded( testRunStats );
70 
71  // Hide totals if there were no errors. This is done because
72  // catkin_tools reports all collected stderr, which produces a lot
73  // of noise.
74  if(testRunStats.totals.testCases.failed)
75  console->testRunEnded(testRunStats);
76  }
77 
78  virtual void testGroupStarting( Catch::GroupInfo const& groupInfo )
79  {
80  stdOutForSuite.str("");
81  stdErrForSuite.str("");
83 
84  CumulativeReporterBase::testGroupStarting( groupInfo );
85  console->testGroupStarting(groupInfo);
86  }
87 
88  virtual void testGroupEnded( Catch::TestGroupStats const& testGroupStats )
89  {
90  CumulativeReporterBase::testGroupEnded( testGroupStats );
91  console->testGroupEnded(testGroupStats);
92  }
93 
94  virtual void sectionStarting( Catch::SectionInfo const& sectionInfo )
95  {
96  CumulativeReporterBase::sectionStarting(sectionInfo);
97  console->sectionStarting(sectionInfo);
98  }
99 
100  virtual void sectionEnded( Catch::SectionStats const& sectionStats )
101  {
102  CumulativeReporterBase::sectionEnded(sectionStats);
103  console->sectionEnded(sectionStats);
104  }
105 
106  virtual void assertionStarting( Catch::AssertionInfo const& assertionInfo )
107  {
108  CumulativeReporterBase::assertionStarting(assertionInfo);
109  console->assertionStarting(assertionInfo);
110  }
111 
112  virtual bool assertionEnded( Catch::AssertionStats const& assertionStats )
113  {
114  if( assertionStats.assertionResult.getResultType() == Catch::ResultWas::ThrewException )
115  {
118  }
119 
120  console->assertionEnded(assertionStats);
121  return CumulativeReporterBase::assertionEnded( assertionStats );
122  }
123 
124  virtual void testCaseStarting( Catch::TestCaseInfo const& testInfo )
125  {
126  CumulativeReporterBase::testCaseStarting(testInfo);
127  console->testCaseStarting(testInfo);
128  }
129 
130  virtual void testCaseEnded( Catch::TestCaseStats const& testCaseStats )
131  {
132  stdOutForSuite << testCaseStats.stdOut;
133  stdErrForSuite << testCaseStats.stdErr;
134 
135  CumulativeReporterBase::testCaseEnded( testCaseStats );
136  console->testCaseEnded(testCaseStats);
137  }
138 
139  virtual void skipTest( Catch::TestCaseInfo const& testInfo )
140  {
141  CumulativeReporterBase::skipTest(testInfo);
142  console->skipTest(testInfo);
143  }
144 
145  virtual void testRunEndedCumulative()
146  {
147  writeRun(*m_testRuns.back());
148  }
149 
150  void writeRun( TestRunNode const& runNode )
151  {
152  Catch::XmlWriter::ScopedElement e = xml.scopedElement( "testsuites" );
153 
154  unsigned int tests = 0;
155  unsigned int failures = 0;
156  for( TestRunNode::ChildNodes::const_iterator
157  it = runNode.children.begin(), itEnd = runNode.children.end();
158  it != itEnd;
159  ++it )
160  {
161  failures += (*it)->value.totals.testCases.failed;
162  tests += (*it)->value.totals.testCases.total();
163  }
164 
165  xml.writeAttribute( "errors", totalUnexpectedExceptions );
166  xml.writeAttribute( "failures", failures - totalUnexpectedExceptions );
167  xml.writeAttribute( "tests", tests );
168 
169  for( TestRunNode::ChildNodes::const_iterator
170  it = runNode.children.begin(), itEnd = runNode.children.end();
171  it != itEnd;
172  ++it )
173  {
174  writeGroup(**it, 0);
175  }
176  }
177 
178  void writeGroup( TestGroupNode const& groupNode, double suiteTime )
179  {
180  Catch::XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
181  Catch::TestGroupStats const& stats = groupNode.value;
182  xml.writeAttribute( "name", m_config->name() );
183  xml.writeAttribute( "errors", unexpectedExceptions );
184  xml.writeAttribute( "failures", stats.totals.testCases.failed-unexpectedExceptions );
185  xml.writeAttribute( "tests", stats.totals.testCases.total() );
186  xml.writeAttribute( "hostname", "tbd" ); // !TBD
187 
188  xml.writeAttribute( "package", catch_ros::meta::packageName() );
189 
190  if( m_config->showDurations() == Catch::ShowDurations::Never )
191  xml.writeAttribute( "time", "" );
192  else
193  xml.writeAttribute( "time", suiteTime );
194  xml.writeAttribute( "timestamp", "tbd" ); // !TBD
195 
196  // Write test cases
197  for( TestGroupNode::ChildNodes::const_iterator
198  it = groupNode.children.begin(), itEnd = groupNode.children.end();
199  it != itEnd;
200  ++it )
201  writeTestCase( **it );
202 
203  xml.scopedElement( "system-out" ).writeText( Catch::trim( stdOutForSuite.str() ), false );
204  xml.scopedElement( "system-err" ).writeText( Catch::trim( stdErrForSuite.str() ), false );
205  }
206 
207  void writeTestCase( TestCaseNode const& testCaseNode )
208  {
209  Catch::TestCaseStats const& stats = testCaseNode.value;
210 
211  // All test cases have exactly one section - which represents the
212  // test case itself. That section may have 0-n nested sections
213  assert( testCaseNode.children.size() == 1 );
214  SectionNode const& rootSection = *testCaseNode.children.front();
215 
216  std::string className = stats.testInfo.className;
217 
218  if( className.empty() )
219  {
220  className = m_config->name();
221  }
222 
223  if(className.empty())
224  className = catch_ros::meta::packageName();
225  else
226  className = std::string(catch_ros::meta::packageName()) + "." + className;
227 
228  writeSection( className, "", rootSection );
229  }
230 
231  void writeSection( std::string const& className,
232  std::string const& rootName,
233  SectionNode const& sectionNode )
234  {
235  std::string name = Catch::trim( sectionNode.stats.sectionInfo.name );
236  if( !rootName.empty() )
237  name = rootName + "/" + name;
238 
239  if( !sectionNode.assertions.empty() ||
240  !sectionNode.stdOut.empty() ||
241  !sectionNode.stdErr.empty() )
242  {
243  Catch::XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
244  if( className.empty() ) {
245  xml.writeAttribute( "classname", name );
246  xml.writeAttribute( "name", "root" );
247  }
248  else {
249  xml.writeAttribute( "classname", className );
250  xml.writeAttribute( "name", name );
251  }
252  xml.writeAttribute( "time", std::to_string( sectionNode.stats.durationInSeconds ) );
253 
254  writeAssertions( sectionNode );
255 
256  if( !sectionNode.stdOut.empty() )
257  xml.scopedElement( "system-out" ).writeText( Catch::trim( sectionNode.stdOut ), false );
258  if( !sectionNode.stdErr.empty() )
259  xml.scopedElement( "system-err" ).writeText( Catch::trim( sectionNode.stdErr ), false );
260  }
261  for( SectionNode::ChildSections::const_iterator
262  it = sectionNode.childSections.begin(),
263  itEnd = sectionNode.childSections.end();
264  it != itEnd;
265  ++it )
266  {
267  if( className.empty() )
268  writeSection( name, "", **it );
269  else
270  writeSection( className, name, **it );
271  }
272  }
273 
274  void writeAssertions( SectionNode const& sectionNode )
275  {
276  for( SectionNode::Assertions::const_iterator
277  it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
278  it != itEnd;
279  ++it )
280  {
281  writeAssertion( *it );
282  }
283  }
284  void writeAssertion( Catch::AssertionStats const& stats )
285  {
286  Catch::AssertionResult const& result = stats.assertionResult;
287  if( !result.isOk() ) {
288  std::string elementName;
289  switch( result.getResultType() ) {
292  elementName = "error";
293  break;
295  elementName = "failure";
296  break;
298  elementName = "failure";
299  break;
301  elementName = "failure";
302  break;
303 
304  // We should never see these here:
311  elementName = "internalError";
312  break;
313  }
314 
315  Catch::XmlWriter::ScopedElement e = xml.scopedElement( elementName );
316 
317  xml.writeAttribute( "message", result.getExpandedExpression() );
318  xml.writeAttribute( "type", result.getTestMacroName() );
319 
320  std::ostringstream oss;
321  if( !result.getMessage().empty() )
322  oss << result.getMessage() << "\n";
323  for( std::vector<Catch::MessageInfo>::const_iterator
324  it = stats.infoMessages.begin(),
325  itEnd = stats.infoMessages.end();
326  it != itEnd;
327  ++it )
328  {
329  if( it->type == Catch::ResultWas::Info )
330  oss << it->message << "\n";
331  }
332 
333  oss << "at " << result.getSourceInfo();
334  xml.writeText( oss.str(), false );
335  }
336  }
337 
338  Catch::XmlWriter xml;
339  std::ostringstream stdOutForSuite;
340  std::ostringstream stdErrForSuite;
341  unsigned int unexpectedExceptions;
343 
344  // Duplicate output to console
345  std::stringstream consoleOut;
346  Catch::ConsoleReporter* console;
347 };
348 
349 CATCH_REGISTER_REPORTER( "ros_junit", ROSReporter )
350 
351 }
352 
353 #endif
void writeRun(TestRunNode const &runNode)
virtual void testRunEnded(Catch::TestRunStats const &testRunStats)
void writeSection(std::string const &className, std::string const &rootName, SectionNode const &sectionNode)
void writeTestCase(TestCaseNode const &testCaseNode)
void writeGroup(TestGroupNode const &groupNode, double suiteTime)
Catch::ConsoleReporter * console
static std::string getDescription()
virtual bool assertionEnded(Catch::AssertionStats const &assertionStats)
const char * packageName()
Definition: meta_info.cpp:10
void writeAssertions(SectionNode const &sectionNode)
unsigned int totalUnexpectedExceptions
std::ostringstream stdErrForSuite
virtual void testCaseEnded(Catch::TestCaseStats const &testCaseStats)
virtual void testGroupEnded(Catch::TestGroupStats const &testGroupStats)
virtual void testCaseStarting(Catch::TestCaseInfo const &testInfo)
virtual void sectionEnded(Catch::SectionStats const &sectionStats)
std::stringstream consoleOut
std::string trim(std::string const &str)
virtual void sectionStarting(Catch::SectionInfo const &sectionInfo)
virtual void testRunEndedCumulative()
virtual void testRunStarting(Catch::TestRunInfo const &runInfo)
virtual void skipTest(Catch::TestCaseInfo const &testInfo)
std::ostringstream stdOutForSuite
std::ostream & cerr()
virtual void assertionStarting(Catch::AssertionInfo const &assertionInfo)
virtual Catch::ReporterPreferences getPreferences() const
ROSReporter(Catch::ReporterConfig const &_config)
virtual void noMatchingTestCases(std::string const &spec)
virtual void testGroupStarting(Catch::GroupInfo const &groupInfo)
void writeAssertion(Catch::AssertionStats const &stats)


catch_ros
Author(s): Max Schwarz
autogenerated on Thu Jan 14 2021 03:32:26