diff --git a/include/catch.hpp b/include/catch.hpp index 4251cd08..9f82c72d 100644 --- a/include/catch.hpp +++ b/include/catch.hpp @@ -11,11 +11,11 @@ #include "internal/catch_suppress_warnings.h" -#ifdef CATCH_CONFIG_MAIN -# define CATCH_CONFIG_RUNNER +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL #endif -#ifdef CATCH_CONFIG_RUNNER +#ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN @@ -43,7 +43,7 @@ #include "internal/catch_objc.hpp" #endif -#ifdef CATCH_CONFIG_RUNNER +#ifdef CATCH_IMPL #include "internal/catch_impl.hpp" #endif diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index 872b65c5..6e058bf0 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -238,6 +238,7 @@ namespace Catch virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + // The return value indicates if the messages buffer should be cleared: virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; virtual void sectionEnded( SectionStats const& sectionStats ) = 0; virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; diff --git a/include/internal/catch_list.hpp b/include/internal/catch_list.hpp index 34ffbc26..1c510ef8 100644 --- a/include/internal/catch_list.hpp +++ b/include/internal/catch_list.hpp @@ -139,7 +139,7 @@ namespace Catch { } inline std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reports:\n"; + Catch::cout() << "Available reporters:\n"; IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; std::size_t maxNameLen = 0; diff --git a/include/reporters/catch_reporter_teamcity.hpp b/include/reporters/catch_reporter_teamcity.hpp new file mode 100644 index 00000000..127ad7b6 --- /dev/null +++ b/include/reporters/catch_reporter_teamcity.hpp @@ -0,0 +1,124 @@ +/* + * Created by Phil Nash on 19th December 2014 + * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED + +#include "catch_reporter_bases.hpp" + +#include "../internal/catch_reporter_registrars.hpp" + +#include + +namespace Catch { + + struct TeamCityReporter : StreamingReporterBase { + TeamCityReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + static bool replace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + std::size_t i = str.find( replaceThis ); + if( i != std::string::npos ) { + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + return true; + } + return false; + } + static std::string escape( std::string const& str ) { + std::string escaped = str; + while( replace( escaped, "\'", "|\'" ) || + replace( escaped, "\n", "|n" ) || + replace( escaped, "\r", "|r" ) || + replace( escaped, "|", "||" ) || + replace( escaped, "[", "|[" ) || + replace( escaped, "]", "|]" ) ); + return escaped; + } + virtual ~TeamCityReporter(); + + static std::string getDescription() { + return "Reports test results as TeamCity service messages"; + } + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = true; + return prefs; + } + + // !TBD: ignored tests + + virtual void noMatchingTestCases( std::string const& /* spec */ ) {} + + virtual void testGroupStarting( GroupInfo const& groupInfo ) { + StreamingReporterBase::testGroupStarting( groupInfo ); + stream << "##teamcity[testSuiteStarted name='" + << escape( groupInfo.name ) << "']\n"; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + StreamingReporterBase::testGroupEnded( testGroupStats ); + stream << "##teamcity[testSuiteFinished name='" + << escape( testGroupStats.groupInfo.name ) << "']\n"; + } + + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + if( !assertionStats.assertionResult.isOk() ) { + stream << "##teamcity[testFailed" + << " name='" << escape( currentTestCaseInfo->name )<< "'" + << " message='message here'" // !TBD + << " details='details?'" // !TBD + << "]\n"; + } + return true; + } + +// virtual void sectionStarting( SectionInfo const& _sectionInfo ) { +// // !TBD +// } +// virtual void sectionEnded( SectionStats const& _sectionStats ) { +// // !TBD +// } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) { + StreamingReporterBase::testCaseStarting( testInfo ); + stream << "##teamcity[testStarted name='" + << escape( testInfo.name ) << "']\n"; + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + StreamingReporterBase::testCaseEnded( testCaseStats ); + if( !testCaseStats.stdOut.empty() ) + stream << "##teamcity[testStdOut name='" + << escape( testCaseStats.testInfo.name ) + << "' out='" << escape( testCaseStats.stdOut ) << "']\n"; + if( !testCaseStats.stdErr.empty() ) + stream << "##teamcity[testStdErr name='" + << escape( testCaseStats.testInfo.name ) + << "' out='" << escape( testCaseStats.stdErr ) << "']\n"; + stream << "##teamcity[testFinished name='" + << escape( testCaseStats.testInfo.name ) << "']\n"; + } +// virtual void testRunEnded( TestRunStats const& _testRunStats ) { +// // !TBD +// } + + private: + + }; + +#ifdef CATCH_IMPL + TeamCityReporter::~TeamCityReporter() {} +#endif + + INTERNAL_CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED diff --git a/projects/SelfTest/TestMain.cpp b/projects/SelfTest/TestMain.cpp index b6fc8690..69176788 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -8,6 +8,7 @@ #define CATCH_CONFIG_MAIN #include "catch.hpp" +#include "reporters/catch_reporter_teamcity.hpp" // Some example tag aliases CATCH_REGISTER_TAG_ALIAS( "[@nhf]", "[failing]~[.]" ) diff --git a/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj index 70f402e1..e4b59f56 100644 --- a/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj +++ b/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj @@ -96,6 +96,7 @@ 26847E5C16BBACB60043B9C1 /* catch_message.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_message.hpp; sourceTree = ""; }; 26847E5D16BBADB40043B9C1 /* catch_message.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_message.cpp; path = ../../../SelfTest/SurrogateCpps/catch_message.cpp; sourceTree = ""; }; 268F47B018A93F7800D8C14F /* catch_clara.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = catch_clara.h; sourceTree = ""; }; + 2691574A1A4480C50054F1ED /* catch_reporter_teamcity.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_teamcity.hpp; sourceTree = ""; }; 26926E8318D7777D004E10F2 /* clara.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = clara.h; path = ../../../../include/external/clara.h; sourceTree = ""; }; 26926E8418D77809004E10F2 /* tbc_text_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tbc_text_format.h; path = ../../../../include/external/tbc_text_format.h; sourceTree = ""; }; 26948284179A9AB900ED166E /* SectionTrackerTests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SectionTrackerTests.cpp; path = ../../../SelfTest/SectionTrackerTests.cpp; sourceTree = ""; }; @@ -313,6 +314,7 @@ 4A6D0C67149B3E3D00DB3EAA /* catch_reporter_junit.hpp */, 4A6D0C68149B3E3D00DB3EAA /* catch_reporter_xml.hpp */, 4AB42F84166F3E1A0099F2C8 /* catch_reporter_console.hpp */, + 2691574A1A4480C50054F1ED /* catch_reporter_teamcity.hpp */, ); name = reporters; path = ../../../../include/reporters;