blob: 68c08e8a547620974d2f079eb6ed78466e9c0567 [file] [log] [blame]
Phil Nash0ab44322010-12-10 08:01:42 +00001/*
2 * catch_reporter_junit.hpp
3 * Catch
4 *
5 * Created by Phil on 26/11/2010.
6 * Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
7 *
8 * Distributed under the Boost Software License, Version 1.0. (See accompanying
9 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
10 *
11 */
12#ifndef TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
13#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
14
15#include "internal/catch_capture.hpp"
16#include "internal/catch_reporter_registry.hpp"
17#include "internal/catch_xmlwriter.hpp"
Phil Nash0ab44322010-12-10 08:01:42 +000018
19namespace Catch
20{
21 class JunitReporter : public Catch::ITestReporter
22 {
Phil Nash0ab44322010-12-10 08:01:42 +000023 struct TestStats
24 {
25 std::string element;
26 std::string resultType;
27 std::string message;
28 std::string content;
29 };
30
31 struct TestCaseStats
32 {
33 TestCaseStats( const std::string& name = std::string() )
34 : name( name )
35 {
36 }
37
38 double timeInSeconds;
39 std::string status;
40 std::string className;
41 std::string name;
42 std::vector<TestStats> testStats;
43 };
44
45 struct Stats
46 {
47 Stats( const std::string& name = std::string() )
48 : testsCount( 0 ),
49 failuresCount( 0 ),
50 disabledCount( 0 ),
51 errorsCount( 0 ),
52 timeInSeconds( 0 ),
53 name( name )
54 {
55 }
56
57 std::size_t testsCount;
58 std::size_t failuresCount;
59 std::size_t disabledCount;
60 std::size_t errorsCount;
61 double timeInSeconds;
62 std::string name;
63
64 std::vector<TestCaseStats> testCaseStats;
65 };
66
67 public:
68 ///////////////////////////////////////////////////////////////////////////
69 JunitReporter( const ReporterConfig& config = ReporterConfig() )
70 : m_config( config ),
71 m_testSuiteStats( "AllTests" ),
72 m_currentStats( &m_testSuiteStats )
73 {
74 }
75
76 ///////////////////////////////////////////////////////////////////////////
77 static std::string getDescription()
78 {
79 return "Reports test results in an XML format that looks like Ant's junitreport target";
80 }
81
82 private: // ITestReporter
83
84 ///////////////////////////////////////////////////////////////////////////
85 virtual void StartTesting()
86 {
87 }
88
89 ///////////////////////////////////////////////////////////////////////////
90 virtual void StartGroup( const std::string& groupName )
91 {
92
Phil Nasha35e4b52010-12-10 08:18:06 +000093 m_statsForSuites.push_back( Stats( groupName ) );
94 m_currentStats = &m_statsForSuites.back();
Phil Nash0ab44322010-12-10 08:01:42 +000095 }
96
97 ///////////////////////////////////////////////////////////////////////////
Phil Nasha35e4b52010-12-10 08:18:06 +000098 virtual void EndGroup( const std::string&, std::size_t succeeded, std::size_t failed )
Phil Nash0ab44322010-12-10 08:01:42 +000099 {
Phil Nasha35e4b52010-12-10 08:18:06 +0000100 m_currentStats->testsCount = failed+succeeded;
Phil Nash0ab44322010-12-10 08:01:42 +0000101 m_currentStats = &m_testSuiteStats;
102 }
103
104 virtual void StartSection( const std::string& sectionName, const std::string description ){(sectionName,description);}
105 virtual void EndSection( const std::string& sectionName, std::size_t succeeded, std::size_t failed ){(sectionName, succeeded, failed);}
106
107 ///////////////////////////////////////////////////////////////////////////
108 virtual void StartTestCase( const Catch::TestCaseInfo& testInfo )
109 {
Phil Nash0ab44322010-12-10 08:01:42 +0000110 m_currentStats->testCaseStats.push_back( TestCaseStats( testInfo.getName() ) );
111
112 }
113
114 ///////////////////////////////////////////////////////////////////////////
115 virtual void Result( const Catch::ResultInfo& resultInfo )
116 {
117 if( !resultInfo.ok() || m_config.includeSuccessfulResults() )
118 {
119 TestCaseStats& testCaseStats = m_currentStats->testCaseStats.back();
120 TestStats stats;
121 std::ostringstream oss;
122 if( !resultInfo.getMessage().empty() )
123 {
124 oss << resultInfo.getMessage() << " at ";
125 }
126 oss << resultInfo.getFilename() << ":" << resultInfo.getLine();
127 stats.content = oss.str();
128 stats.message = resultInfo.getExpandedExpression();
129 stats.resultType = resultInfo.getTestMacroName();
130 switch( resultInfo.getResultType() )
131 {
132 case ResultWas::ThrewException:
133 stats.element = "error";
Phil Nasha35e4b52010-12-10 08:18:06 +0000134 m_currentStats->errorsCount++;
Phil Nash0ab44322010-12-10 08:01:42 +0000135 break;
136 case ResultWas::Info:
137 stats.element = "info"; // !TBD ?
138 break;
139 case ResultWas::Warning:
140 stats.element = "warning"; // !TBD ?
141 break;
142 case ResultWas::ExplicitFailure:
143 stats.element = "failure";
Phil Nasha35e4b52010-12-10 08:18:06 +0000144 m_currentStats->failuresCount++;
Phil Nash0ab44322010-12-10 08:01:42 +0000145 break;
146 case ResultWas::ExpressionFailed:
147 stats.element = "failure";
Phil Nasha35e4b52010-12-10 08:18:06 +0000148 m_currentStats->failuresCount++;
Phil Nash0ab44322010-12-10 08:01:42 +0000149 break;
150 case ResultWas::Ok:
151 stats.element = "success";
152 break;
153 default:
154 stats.element = "unknown";
155 break;
156 }
157 testCaseStats.testStats.push_back( stats );
158
159 }
160 }
161
162 ///////////////////////////////////////////////////////////////////////////
163 virtual void EndTestCase( const Catch::TestCaseInfo&, const std::string& stdOut, const std::string& stdErr )
164 {
165 if( !stdOut.empty() )
166 m_stdOut << stdOut << "\n";
167 if( !stdErr.empty() )
168 m_stdErr << stdErr << "\n";
169 }
170
Phil Nash0ab44322010-12-10 08:01:42 +0000171 ///////////////////////////////////////////////////////////////////////////
172 virtual void EndTesting( std::size_t /* succeeded */, std::size_t /* failed */ )
173 {
174 std::ostream& str = m_config.stream();
175 {
176 XmlWriter xml( str );
177
178 if( m_statsForSuites.size() > 0 )
179 xml.startElement( "testsuites" );
180
181 std::vector<Stats>::const_iterator it = m_statsForSuites.begin();
182 std::vector<Stats>::const_iterator itEnd = m_statsForSuites.end();
183
184 for(; it != itEnd; ++it )
185 {
186 XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
187 xml.writeAttribute( "name", it->name );
Phil Nasha35e4b52010-12-10 08:18:06 +0000188 xml.writeAttribute( "errors", it->errorsCount );
189 xml.writeAttribute( "failures", it->failuresCount );
190 xml.writeAttribute( "tests", it->testsCount );
191 xml.writeAttribute( "hostname", "tbd" );
192 xml.writeAttribute( "time", "tbd" );
193 xml.writeAttribute( "timestamp", "tbd" );
Phil Nash0ab44322010-12-10 08:01:42 +0000194
195 OutputTestCases( xml, *it );
196 }
197
198 xml.scopedElement( "system-out" ).writeText( trim( m_stdOut.str() ) );
199 xml.scopedElement( "system-err" ).writeText( trim( m_stdOut.str() ) );
200 }
201 }
202
203 ///////////////////////////////////////////////////////////////////////////
204 void OutputTestCases( XmlWriter& xml, const Stats& stats )
205 {
206 std::vector<TestCaseStats>::const_iterator it = stats.testCaseStats.begin();
207 std::vector<TestCaseStats>::const_iterator itEnd = stats.testCaseStats.end();
208 for(; it != itEnd; ++it )
209 {
210 xml.writeBlankLine();
211 xml.writeComment( "Test case" );
212
213 XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
214 xml.writeAttribute( "classname", it->className );
215 xml.writeAttribute( "name", it->name );
216 xml.writeAttribute( "time", "tbd" );
217
218 OutputTestResult( xml, *it );
219 }
220 }
221
222
223 ///////////////////////////////////////////////////////////////////////////
224 void OutputTestResult( XmlWriter& xml, const TestCaseStats& stats )
225 {
226 std::vector<TestStats>::const_iterator it = stats.testStats.begin();
227 std::vector<TestStats>::const_iterator itEnd = stats.testStats.end();
228 for(; it != itEnd; ++it )
229 {
230 if( it->element != "success" )
231 {
232 XmlWriter::ScopedElement e = xml.scopedElement( it->element );
233
234 xml.writeAttribute( "message", it->message );
235 xml.writeAttribute( "type", it->resultType );
236 if( !it->content.empty() )
237 xml.writeText( it->content );
238 }
239 }
240 }
241
242 private:
243 const ReporterConfig& m_config;
244 bool m_currentTestSuccess;
245
246 Stats m_testSuiteStats;
247 Stats* m_currentStats;
248 std::vector<Stats> m_statsForSuites;
249 std::ostringstream m_stdOut;
250 std::ostringstream m_stdErr;
Phil Nash0ab44322010-12-10 08:01:42 +0000251 };
252
253} // end namespace Catch
254
255#endif // TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED