Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
catch_reporter_junit.cpp
Go to the documentation of this file.
1/*
2 * Created by Phil on 26/11/2010.
3 * Copyright 2010 Two Blue Cubes Ltd. 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
10
12
15
16#include <cassert>
17#include <sstream>
18#include <ctime>
19#include <algorithm>
20
21namespace Catch {
22
23 namespace {
24 std::string getCurrentTimestamp() {
25 // Beware, this is not reentrant because of backward compatibility issues
26 // Also, UTC only, again because of backward compatibility (%z is C++11)
27 time_t rawtime;
28 std::time(&rawtime);
29 auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
30
31#ifdef _MSC_VER
32 std::tm timeInfo = {};
33 gmtime_s(&timeInfo, &rawtime);
34#else
35 std::tm* timeInfo;
36 timeInfo = std::gmtime(&rawtime);
37#endif
38
39 char timeStamp[timeStampSize];
40 const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
41
42#ifdef _MSC_VER
43 std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
44#else
45 std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
46#endif
47 return std::string(timeStamp);
48 }
49
50 std::string fileNameTag(const std::vector<std::string> &tags) {
51 auto it = std::find_if(begin(tags),
52 end(tags),
53 [] (std::string const& tag) {return tag.front() == '#'; });
54 if (it != tags.end())
55 return it->substr(1);
56 return std::string();
57 }
58 } // anonymous namespace
59
61 : CumulativeReporterBase( _config ),
62 xml( _config.stream() )
63 {
66 }
67
69
71 return "Reports test results in an XML format that looks like Ant's junitreport target";
72 }
73
74 void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
75
78 xml.startElement( "testsuites" );
79 }
80
88
89 void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
90 m_okToFail = testCaseInfo.okToFail();
91 }
92
93 bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
96 return CumulativeReporterBase::assertionEnded( assertionStats );
97 }
98
99 void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
100 stdOutForSuite += testCaseStats.stdOut;
101 stdErrForSuite += testCaseStats.stdErr;
103 }
104
105 void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
106 double suiteTime = suiteTimer.getElapsedSeconds();
108 writeGroup( *m_testGroups.back(), suiteTime );
109 }
110
114
115 void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
116 XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
117
118 TestGroupStats const& stats = groupNode.value;
119 xml.writeAttribute( "name", stats.groupInfo.name );
121 xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
122 xml.writeAttribute( "tests", stats.totals.assertions.total() );
123 xml.writeAttribute( "hostname", "tbd" ); // !TBD
124 if( m_config->showDurations() == ShowDurations::Never )
125 xml.writeAttribute( "time", "" );
126 else
127 xml.writeAttribute( "time", suiteTime );
128 xml.writeAttribute( "timestamp", getCurrentTimestamp() );
129
130 // Write properties if there are any
131 if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
132 auto properties = xml.scopedElement("properties");
133 if (m_config->hasTestFilters()) {
134 xml.scopedElement("property")
135 .writeAttribute("name", "filters")
136 .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
137 }
138 if (m_config->rngSeed() != 0) {
139 xml.scopedElement("property")
140 .writeAttribute("name", "random-seed")
141 .writeAttribute("value", m_config->rngSeed());
142 }
143 }
144
145 // Write test cases
146 for( auto const& child : groupNode.children )
147 writeTestCase( *child );
148
149 xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
150 xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
151 }
152
153 void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
154 TestCaseStats const& stats = testCaseNode.value;
155
156 // All test cases have exactly one section - which represents the
157 // test case itself. That section may have 0-n nested sections
158 assert( testCaseNode.children.size() == 1 );
159 SectionNode const& rootSection = *testCaseNode.children.front();
160
161 std::string className = stats.testInfo.className;
162
163 if( className.empty() ) {
164 className = fileNameTag(stats.testInfo.tags);
165 if ( className.empty() )
166 className = "global";
167 }
168
169 if ( !m_config->name().empty() )
170 className = m_config->name() + "." + className;
171
172 writeSection( className, "", rootSection );
173 }
174
175 void JunitReporter::writeSection( std::string const& className,
176 std::string const& rootName,
177 SectionNode const& sectionNode ) {
178 std::string name = trim( sectionNode.stats.sectionInfo.name );
179 if( !rootName.empty() )
180 name = rootName + '/' + name;
181
182 if( !sectionNode.assertions.empty() ||
183 !sectionNode.stdOut.empty() ||
184 !sectionNode.stdErr.empty() ) {
186 if( className.empty() ) {
187 xml.writeAttribute( "classname", name );
188 xml.writeAttribute( "name", "root" );
189 }
190 else {
191 xml.writeAttribute( "classname", className );
192 xml.writeAttribute( "name", name );
193 }
194 xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
195
196 writeAssertions( sectionNode );
197
198 if( !sectionNode.stdOut.empty() )
199 xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
200 if( !sectionNode.stdErr.empty() )
201 xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
202 }
203 for( auto const& childNode : sectionNode.childSections )
204 if( className.empty() )
205 writeSection( name, "", *childNode );
206 else
207 writeSection( className, name, *childNode );
208 }
209
210 void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
211 for( auto const& assertion : sectionNode.assertions )
212 writeAssertion( assertion );
213 }
214
216 AssertionResult const& result = stats.assertionResult;
217 if( !result.isOk() ) {
218 std::string elementName;
219 switch( result.getResultType() ) {
222 elementName = "error";
223 break;
225 elementName = "failure";
226 break;
228 elementName = "failure";
229 break;
231 elementName = "failure";
232 break;
233
234 // We should never see these here:
235 case ResultWas::Info:
237 case ResultWas::Ok:
241 elementName = "internalError";
242 break;
243 }
244
245 XmlWriter::ScopedElement e = xml.scopedElement( elementName );
246
247 xml.writeAttribute( "message", result.getExpandedExpression() );
248 xml.writeAttribute( "type", result.getTestMacroName() );
249
251 if( !result.getMessage().empty() )
252 rss << result.getMessage() << '\n';
253 for( auto const& msg : stats.infoMessages )
254 if( msg.type == ResultWas::Info )
255 rss << msg.message << '\n';
256
257 rss << "at " << result.getSourceInfo();
258 xml.writeText( rss.str(), false );
259 }
260 }
261
263
264} // end namespace Catch
std::string name
#define CATCH_REGISTER_REPORTER(name, reporterType)
ResultWas::OfType getResultType() const
void writeGroup(TestGroupNode const &groupNode, double suiteTime)
void testCaseStarting(TestCaseInfo const &testCaseInfo) override
JunitReporter(ReporterConfig const &_config)
void testGroupStarting(GroupInfo const &groupInfo) override
void testRunStarting(TestRunInfo const &runInfo) override
void testRunEndedCumulative() override
void writeAssertion(AssertionStats const &stats)
void writeSection(std::string const &className, std::string const &rootName, SectionNode const &sectionNode)
void testGroupEnded(TestGroupStats const &testGroupStats) override
void writeAssertions(SectionNode const &sectionNode)
void testCaseEnded(TestCaseStats const &testCaseStats) override
void noMatchingTestCases(std::string const &) override
void writeTestCase(TestCaseNode const &testCaseNode)
static std::string getDescription()
bool assertionEnded(AssertionStats const &assertionStats) override
auto str() const -> std::string
auto getElapsedSeconds() const -> double
ScopedElement & writeText(std::string const &text, bool indent=true)
ScopedElement & writeAttribute(std::string const &name, T const &attribute)
ScopedElement scopedElement(std::string const &name)
XmlWriter & writeText(std::string const &text, bool indent=true)
XmlWriter & startElement(std::string const &name)
XmlWriter & endElement()
XmlWriter & writeAttribute(std::string const &name, std::string const &attribute)
std::string stringify(const T &e)
std::string trim(std::string const &str)
std::string serializeFilters(std::vector< std::string > const &container)
not_this_one end(...)
not_this_one begin(...)
Node< TestGroupStats, TestCaseNode > TestGroupNode
void testGroupStarting(GroupInfo const &) override
void testCaseEnded(TestCaseStats const &testCaseStats) override
void testGroupEnded(TestGroupStats const &testGroupStats) override
std::vector< std::shared_ptr< TestGroupNode > > m_testGroups
void testRunStarting(TestRunInfo const &) override
bool assertionEnded(AssertionStats const &assertionStats) override