Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
catch_reporter_teamcity.hpp
Go to the documentation of this file.
1/*
2 * Created by Phil Nash on 19th December 2014
3 * Copyright 2014 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#ifndef TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
9#define TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
10
11// Don't #include any Catch headers here - we can assume they are already
12// included before this header.
13// This is not good practice in general but is necessary in this case so this
14// file can be distributed as a single header that works with the main
15// Catch single header.
16
17#include <cstring>
18
19#ifdef __clang__
20# pragma clang diagnostic push
21# pragma clang diagnostic ignored "-Wpadded"
22#endif
23
24namespace Catch {
25
26 struct TeamCityReporter : StreamingReporterBase<TeamCityReporter> {
28 : StreamingReporterBase( _config )
29 {
31 }
32
33 static std::string escape( std::string const& str ) {
34 std::string escaped = str;
35 replaceInPlace( escaped, "|", "||" );
36 replaceInPlace( escaped, "'", "|'" );
37 replaceInPlace( escaped, "\n", "|n" );
38 replaceInPlace( escaped, "\r", "|r" );
39 replaceInPlace( escaped, "[", "|[" );
40 replaceInPlace( escaped, "]", "|]" );
41 return escaped;
42 }
44
45 static std::string getDescription() {
46 return "Reports test results as TeamCity service messages";
47 }
48
49 void skipTest( TestCaseInfo const& /* testInfo */ ) override {
50 }
51
52 void noMatchingTestCases( std::string const& /* spec */ ) override {}
53
54 void testGroupStarting( GroupInfo const& groupInfo ) override {
56 stream << "##teamcity[testSuiteStarted name='"
57 << escape( groupInfo.name ) << "']\n";
58 }
59 void testGroupEnded( TestGroupStats const& testGroupStats ) override {
61 stream << "##teamcity[testSuiteFinished name='"
62 << escape( testGroupStats.groupInfo.name ) << "']\n";
63 }
64
65
66 void assertionStarting( AssertionInfo const& ) override {}
67
68 bool assertionEnded( AssertionStats const& assertionStats ) override {
69 AssertionResult const& result = assertionStats.assertionResult;
70 if( !result.isOk() ) {
71
73 if( !m_headerPrintedForThisSection )
74 printSectionHeader( msg.get() );
75 m_headerPrintedForThisSection = true;
76
77 msg << result.getSourceInfo() << "\n";
78
79 switch( result.getResultType() ) {
81 msg << "expression failed";
82 break;
84 msg << "unexpected exception";
85 break;
87 msg << "fatal error condition";
88 break;
90 msg << "no exception was thrown where one was expected";
91 break;
93 msg << "explicit failure";
94 break;
95
96 // We shouldn't get here because of the isOk() test
97 case ResultWas::Ok:
98 case ResultWas::Info:
100 CATCH_ERROR( "Internal error in TeamCity reporter" );
101 // These cases are here to prevent compiler warnings
105 CATCH_ERROR( "Not implemented" );
106 }
107 if( assertionStats.infoMessages.size() == 1 )
108 msg << " with message:";
109 if( assertionStats.infoMessages.size() > 1 )
110 msg << " with messages:";
111 for( auto const& messageInfo : assertionStats.infoMessages )
112 msg << "\n \"" << messageInfo.message << "\"";
113
114
115 if( result.hasExpression() ) {
116 msg <<
117 "\n " << result.getExpressionInMacro() << "\n"
118 "with expansion:\n" <<
119 " " << result.getExpandedExpression() << "\n";
120 }
121
122 if( currentTestCaseInfo->okToFail() ) {
123 msg << "- failure ignore as test marked as 'ok to fail'\n";
124 stream << "##teamcity[testIgnored"
125 << " name='" << escape( currentTestCaseInfo->name )<< "'"
126 << " message='" << escape( msg.str() ) << "'"
127 << "]\n";
128 }
129 else {
130 stream << "##teamcity[testFailed"
131 << " name='" << escape( currentTestCaseInfo->name )<< "'"
132 << " message='" << escape( msg.str() ) << "'"
133 << "]\n";
134 }
135 }
136 stream.flush();
137 return true;
138 }
139
140 void sectionStarting( SectionInfo const& sectionInfo ) override {
141 m_headerPrintedForThisSection = false;
143 }
144
145 void testCaseStarting( TestCaseInfo const& testInfo ) override {
146 m_testTimer.start();
148 stream << "##teamcity[testStarted name='"
149 << escape( testInfo.name ) << "']\n";
150 stream.flush();
151 }
152
153 void testCaseEnded( TestCaseStats const& testCaseStats ) override {
155 if( !testCaseStats.stdOut.empty() )
156 stream << "##teamcity[testStdOut name='"
157 << escape( testCaseStats.testInfo.name )
158 << "' out='" << escape( testCaseStats.stdOut ) << "']\n";
159 if( !testCaseStats.stdErr.empty() )
160 stream << "##teamcity[testStdErr name='"
161 << escape( testCaseStats.testInfo.name )
162 << "' out='" << escape( testCaseStats.stdErr ) << "']\n";
163 stream << "##teamcity[testFinished name='"
164 << escape( testCaseStats.testInfo.name ) << "' duration='"
165 << m_testTimer.getElapsedMilliseconds() << "']\n";
166 stream.flush();
167 }
168
169 private:
170 void printSectionHeader( std::ostream& os ) {
171 assert( !m_sectionStack.empty() );
172
173 if( m_sectionStack.size() > 1 ) {
174 os << getLineOfChars<'-'>() << "\n";
175
176 std::vector<SectionInfo>::const_iterator
177 it = m_sectionStack.begin()+1, // Skip first section (test case)
178 itEnd = m_sectionStack.end();
179 for( ; it != itEnd; ++it )
180 printHeaderString( os, it->name );
181 os << getLineOfChars<'-'>() << "\n";
182 }
183
184 SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
185
186 if( !lineInfo.empty() )
187 os << lineInfo << "\n";
188 os << getLineOfChars<'.'>() << "\n\n";
189 }
190
191 // if string has a : in first line will set indent to follow it on
192 // subsequent lines
193 static void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) {
194 std::size_t i = _string.find( ": " );
195 if( i != std::string::npos )
196 i+=2;
197 else
198 i = 0;
199 os << Column( _string )
200 .indent( indent+i)
201 .initialIndent( indent ) << "\n";
202 }
203 private:
204 bool m_headerPrintedForThisSection = false;
205 Timer m_testTimer;
206 };
207
208#ifdef CATCH_IMPL
210#endif
211
212 CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter )
213
214} // end namespace Catch
215
216#ifdef __clang__
217# pragma clang diagnostic pop
218#endif
219
220#endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED
#define CATCH_ERROR(msg)
#define CATCH_REGISTER_REPORTER(name, reporterType)
auto str() const -> std::string
auto get() -> std::ostream &
auto getElapsedMilliseconds() const -> unsigned int
os_t os
char const * getLineOfChars()
bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis)
std::vector< MessageInfo > infoMessages
void sectionStarting(SectionInfo const &_sectionInfo) override
void testGroupStarting(GroupInfo const &_groupInfo) override
void testCaseStarting(TestCaseInfo const &_testInfo) override
void testCaseEnded(TestCaseStats const &) override
void testGroupEnded(TestGroupStats const &) override
void noMatchingTestCases(std::string const &) override
void testGroupEnded(TestGroupStats const &testGroupStats) override
void assertionStarting(AssertionInfo const &) override
void skipTest(TestCaseInfo const &) override
void testCaseStarting(TestCaseInfo const &testInfo) override
static std::string escape(std::string const &str)
bool assertionEnded(AssertionStats const &assertionStats) override
void sectionStarting(SectionInfo const &sectionInfo) override
void testGroupStarting(GroupInfo const &groupInfo) override
~TeamCityReporter() override
TeamCityReporter(ReporterConfig const &_config)
static std::string getDescription()
void testCaseEnded(TestCaseStats const &testCaseStats) override