prepare v1.1.4
[awesomized/libmemcached] / test / lib / catch_reporter_teamcity.hpp
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
24 namespace Catch {
25
26 struct TeamCityReporter : StreamingReporterBase<TeamCityReporter> {
27 TeamCityReporter( ReporterConfig const& _config )
28 : StreamingReporterBase( _config )
29 {
30 m_reporterPrefs.shouldRedirectStdOut = true;
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 }
43 ~TeamCityReporter() override;
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 {
55 StreamingReporterBase::testGroupStarting( groupInfo );
56 stream << "##teamcity[testSuiteStarted name='"
57 << escape( groupInfo.name ) << "']\n";
58 }
59 void testGroupEnded( TestGroupStats const& testGroupStats ) override {
60 StreamingReporterBase::testGroupEnded( testGroupStats );
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
72 ReusableStringStream msg;
73 if( !m_headerPrintedForThisSection )
74 printSectionHeader( msg.get() );
75 m_headerPrintedForThisSection = true;
76
77 msg << result.getSourceInfo() << "\n";
78
79 switch( result.getResultType() ) {
80 case ResultWas::ExpressionFailed:
81 msg << "expression failed";
82 break;
83 case ResultWas::ThrewException:
84 msg << "unexpected exception";
85 break;
86 case ResultWas::FatalErrorCondition:
87 msg << "fatal error condition";
88 break;
89 case ResultWas::DidntThrowException:
90 msg << "no exception was thrown where one was expected";
91 break;
92 case ResultWas::ExplicitFailure:
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:
99 case ResultWas::Warning:
100 CATCH_ERROR( "Internal error in TeamCity reporter" );
101 // These cases are here to prevent compiler warnings
102 case ResultWas::Unknown:
103 case ResultWas::FailureBit:
104 case ResultWas::Exception:
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;
142 StreamingReporterBase::sectionStarting( sectionInfo );
143 }
144
145 void testCaseStarting( TestCaseInfo const& testInfo ) override {
146 m_testTimer.start();
147 StreamingReporterBase::testCaseStarting( testInfo );
148 stream << "##teamcity[testStarted name='"
149 << escape( testInfo.name ) << "']\n";
150 stream.flush();
151 }
152
153 void testCaseEnded( TestCaseStats const& testCaseStats ) override {
154 StreamingReporterBase::testCaseEnded( testCaseStats );
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 os << lineInfo << "\n";
187 os << getLineOfChars<'.'>() << "\n\n";
188 }
189
190 // if string has a : in first line will set indent to follow it on
191 // subsequent lines
192 static void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) {
193 std::size_t i = _string.find( ": " );
194 if( i != std::string::npos )
195 i+=2;
196 else
197 i = 0;
198 os << Column( _string )
199 .indent( indent+i)
200 .initialIndent( indent ) << "\n";
201 }
202 private:
203 bool m_headerPrintedForThisSection = false;
204 Timer m_testTimer;
205 };
206
207 #ifdef CATCH_IMPL
208 TeamCityReporter::~TeamCityReporter() {}
209 #endif
210
211 CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter )
212
213 } // end namespace Catch
214
215 #ifdef __clang__
216 # pragma clang diagnostic pop
217 #endif
218
219 #endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED