diff --git a/docs/list-of-examples.md b/docs/list-of-examples.md index 1284b2d6b0..5b538da032 100644 --- a/docs/list-of-examples.md +++ b/docs/list-of-examples.md @@ -3,12 +3,15 @@ ## Already available +- Catch main: [Catch-provided main](../examples/000-CatchMain.cpp) - Test Case: [Single-file](../examples/010-TestCase.cpp) - Test Case: [Multiple-file 1](../examples/020-TestCase-1.cpp), [2](../examples/020-TestCase-2.cpp) - Assertion: [REQUIRE, CHECK](../examples/030-Asn-Require-Check.cpp) - Fixture: [Sections](../examples/100-Fix-Section.cpp) - Fixture: [Class-based fixtures](../examples/110-Fix-ClassFixture.cpp) - BDD: [SCENARIO, GIVEN, WHEN, THEN](../examples/120-Bdd-ScenarioGivenWhenThen.cpp) +- Report: [Catch-provided main](../examples/200-Rpt-CatchMain.cpp) +- Report: [TeamCity reporter](../examples/207-Rpt-TeamCityReporter.cpp) - Listener: [Listeners](../examples/210-Evt-EventListeners.cpp) - Configuration: [Provide your own output streams](../examples/231-Cfg-OutputStreams.cpp) @@ -27,7 +30,10 @@ - Logging: [FAIL, FAIL_CHECK - Issue message and force failure/continue](../examples/170-Log-Fail.cpp) - Logging: [SUCCEED - Issue message and continue](../examples/180-Log-Succeed.cpp) - Report: [User-defined type](../examples/190-Rpt-ReportUserDefinedType.cpp) -- Report: [Reporter](../examples/200-Rpt-UserDefinedReporter.cpp) +- Report: [User-defined reporter](../examples/202-Rpt-UserDefinedReporter.cpp) +- Report: [Automake reporter](../examples/205-Rpt-AutomakeReporter.cpp) +- Report: [TAP reporter](../examples/206-Rpt-TapReporter.cpp) +- Report: [Multiple reporter](../examples/208-Rpt-MultipleReporters.cpp) - Configuration: [Provide your own main()](../examples/220-Cfg-OwnMain.cpp) - Configuration: [Compile-time configuration](../examples/230-Cfg-CompileTimeConfiguration.cpp) - Configuration: [Run-time configuration](../examples/240-Cfg-RunTimeConfiguration.cpp) diff --git a/docs/reporters.md b/docs/reporters.md index 78e78ee925..32b3419b5a 100644 --- a/docs/reporters.md +++ b/docs/reporters.md @@ -25,8 +25,8 @@ Because of the way the junit format is structured the run must complete before a There are a few additional reporters, for specific build systems, in the Catch repository (in `include\reporters`) which you can `#include` in your project if you would like to make use of them. Do this in one source file - the same one you have `CATCH_CONFIG_MAIN` or `CATCH_CONFIG_RUNNER`. -* `teamcity` writes the native, streaming, format that [TeamCity](https://www.jetbrains.com/teamcity/) understands. -Use this when building as part of a TeamCity build to see results as they happen. +* `teamcity` writes the native, streaming, format that [TeamCity](https://www.jetbrains.com/teamcity/) understands. +Use this when building as part of a TeamCity build to see results as they happen ([code example](../examples/207-Rpt-TeamCityReporter.cpp)). * `tap` writes in the TAP ([Test Anything Protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol)) format. * `automake` writes in a format that correspond to [automake .trs](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html) files diff --git a/examples/200-Rpt-CatchMain.cpp b/examples/200-Rpt-CatchMain.cpp new file mode 100644 index 0000000000..b84c804dd5 --- /dev/null +++ b/examples/200-Rpt-CatchMain.cpp @@ -0,0 +1,27 @@ +// 200-Rpt-CatchMain.cpp + +// In a Catch project with multiple files, dedicate one file to compile the +// source code of Catch itself and reuse the resulting object file for linking. + +// Let Catch provide main(): +#define CATCH_CONFIG_MAIN + +#include + +#ifdef CATCH_EXAMPLE_RPT_1 +#include CATCH_EXAMPLE_RPT_1 +#endif + +#ifdef CATCH_EXAMPLE_RPT_2 +#include CATCH_EXAMPLE_RPT_2 +#endif + +#ifdef CATCH_EXAMPLE_RPT_3 +#include CATCH_EXAMPLE_RPT_3 +#endif + +// That's it + +// Compile implementation of Catch for use with files that do contain tests: +// - g++ -std=c++11 -Wall -I$(CATCH_ROOT) -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -o 200-Rpt-CatchMainTeamCity.o -c 200-Rpt-CatchMain.cpp +// cl -EHsc -I%CATCH_ROOT% -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -Fo200-Rpt-CatchMainTeamCity.obj -c 200-Rpt-CatchMain.cpp diff --git a/examples/207-Rpt-TeamCityReporter.cpp b/examples/207-Rpt-TeamCityReporter.cpp new file mode 100644 index 0000000000..d28460f400 --- /dev/null +++ b/examples/207-Rpt-TeamCityReporter.cpp @@ -0,0 +1,171 @@ +// 207-Rpt-TeamCityReporter.cpp + +// Catch has built-in and external reporters: +// Built-in: +// - compact +// - console +// - junit +// - xml +// External: +// - automake +// - tap +// - teamcity (this example) + +// main() and reporter code provided in 200-Rpt-CatchMain.cpp + +#include + +#ifdef _MSC_VER +# pragma warning (disable : 4702) // Disable warning: unreachable code +#endif + +TEST_CASE( "TeamCity passes unconditionally succeeding assertion", "[teamcity]" ) { + + SUCCEED(); +} + +TEST_CASE( "TeamCity reports unconditionally failing assertion", "[teamcity]" ) { + + FAIL(); +} + +TEST_CASE( "TeamCity reports failing check", "[teamcity]" ) { + + REQUIRE( 3 == 7 ); +} + +TEST_CASE( "TeamCity reports failing check-false", "[teamcity]" ) { + + REQUIRE_FALSE( 3 == 3 ); +} + +TEST_CASE( "TeamCity reports failing check-that", "[teamcity]" ) { + + using namespace Catch; + + REQUIRE_THAT( "hello", Contains( "world" ) ); +} + +TEST_CASE( "TeamCity reports unexpected exception", "[teamcity]" ) { + + REQUIRE( (throw std::runtime_error("surprise!"), true) ); +} + +TEST_CASE( "TeamCity reports undesired exception", "[teamcity]" ) { + + REQUIRE_NOTHROW( (throw std::runtime_error("surprise!"), true) ); +} + +TEST_CASE( "TeamCity reports missing expected exception", "[teamcity]" ) { + + REQUIRE_THROWS( true ); +} + +TEST_CASE( "TeamCity reports missing specific expected exception", "[teamcity]" ) { + + REQUIRE_THROWS_AS( throw std::bad_alloc(), std::runtime_error ); +} + +TEST_CASE( "TeamCity reports unexpected message in expected exception", "[teamcity]" ) { + + using namespace Catch; + + CHECK_THROWS_WITH( throw std::runtime_error("hello"), "world" ); + CHECK_THROWS_WITH( throw std::runtime_error("hello"), Contains("world") ); +} + +struct MyException: public std::runtime_error +{ + MyException( char const * text ) + : std::runtime_error( text ) {} + + ~MyException() override; +}; + +// prevent -Wweak-vtables: +MyException::~MyException() = default; + +struct MyExceptionMatcher : Catch::MatcherBase< std::runtime_error > +{ + std::string m_text; + + MyExceptionMatcher( char const * text ) + : m_text( text ) + {} + + ~MyExceptionMatcher() override; + + bool match( std::runtime_error const & arg ) const override + { + return m_text == arg.what() ; + } + + std::string describe() const override + { + return "it's me"; + } +}; + +// prevent -Wweak-vtables: +MyExceptionMatcher::~MyExceptionMatcher() = default; + +TEST_CASE( "TeamCity failing check-throws-matches", "[teamcity]" ) { + + CHECK_THROWS_MATCHES( throw MyException("hello"), MyException, MyExceptionMatcher("world") ); +} + +// [!throws] - lets Catch know that this test is likely to throw an exception even if successful. +// This causes the test to be excluded when running with -e or --nothrow. + +// No special effects for the reporter. + +TEST_CASE( "TeamCity throwing exception with tag [!throws]", "[teamcity][!throws]" ) { + + REQUIRE_THROWS( throw std::runtime_error("unsurprisingly") ); +} + +// [!mayfail] - doesn't fail the test if any given assertion fails (but still reports it). This can be useful to flag a work-in-progress, or a known issue that you don't want to immediately fix but still want to track in your tests. + +TEST_CASE( "TeamCity failing assertion with tag [!mayfail]", "[teamcity][!mayfail] " ) { + + REQUIRE( 3 == 7 ); // doesn't fail test case this time, reports: testIgnored + REQUIRE( 3 == 3 ); +} + +// [!shouldfail] - like [!mayfail] but fails the test if it passes. +// This can be useful if you want to be notified of accidental, or third-party, fixes. + +TEST_CASE( "TeamCity succeeding assertion with tag [!shouldfail]", "[teamcity][!shouldfail]" ) { + + SUCCEED( "Marked [!shouldfail]" ); +} + +// Compile & run: +// - g++ -std=c++11 -Wall -I$(CATCH_ROOT) -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -o 200-Rpt-CatchMainTeamCity.o -c 200-Rpt-CatchMain.cpp +// - g++ -std=c++11 -Wall -I$(CATCH_ROOT) -o 207-Rpt-TeamCityReporter 207-Rpt-TeamCityReporter.cpp 200-Rpt-CatchMainTeamCity.o && 207-Rpt-TeamCityReporter --list-reporters +// +// - cl -EHsc -I%CATCH_ROOT% -DCATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" -Fo200-Rpt-CatchMainTeamCity.obj -c 200-Rpt-CatchMain.cpp +// - cl -EHsc -I%CATCH_ROOT% 207-Rpt-TeamCityReporter.cpp 200-Rpt-CatchMainTeamCity.o && 207-Rpt-TeamCityReporter --list-reporters + +// Compilation output (--list-reporters): +// Available reporters: +// compact: Reports test results on a single line, suitable for IDEs +// console: Reports test results as plain lines of text +// junit: Reports test results in an XML format that looks like Ant's +// junitreport target +// teamcity: Reports test results as TeamCity service messages +// xml: Reports test results as an XML document + +// Expected output (abbreviated and broken into shorter lines): +// +// prompt> 207-Rpt-TeamCityReporter.exe --reporter teamcity +// ##teamcity[testSuiteStarted name='207-Rpt-TeamCityReporter.exe'] +// ##teamcity[testStarted name='TeamCity passes unconditionally succeeding assertion'] +// ##teamcity[testFinished name='TeamCity passes unconditionally succeeding assertion' duration='1'] +// ##teamcity[testStarted name='TeamCity reports unconditionally failing assertion'] +// ##teamcity[testFailed name='TeamCity reports unconditionally failing assertion' / +// message='.../examples/207-Rpt-TeamCityReporter.cpp:23|n/ +// ...............................................................................|n|n/ +// .../examples/207-Rpt-TeamCityReporter.cpp:25|nexplicit failure'] +// ##teamcity[testFinished name='TeamCity reports unconditionally failing assertion' duration='3'] +// ... diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index aa89ab458d..2551e77c07 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,7 @@ message( STATUS "Examples included" ) set( EXAMPLES_DIR ${CATCH_DIR}/examples ) set( HEADER_DIR ${CATCH_DIR}/single_include ) +set( REPORTER_HEADER_DIR ${CATCH_DIR}/include/reporters ) # single-file sources: @@ -45,6 +46,26 @@ set( SOURCES_IDIOMATIC_TESTS 210-Evt-EventListeners.cpp ) +# main-s for reporter-specific test sources: + +set( SOURCES_REPORTERS_MAIN + 200-Rpt-CatchMain.cpp +) + +string( REPLACE ".cpp" "" BASENAMES_REPORTERS_MAIN 200-Rpt-CatchMain.cpp ) + +set( NAMES_REPORTERS TeamCity ) + +foreach( reporter ${NAMES_REPORTERS} ) + list( APPEND SOURCES_SPECIFIC_REPORTERS_MAIN ${BASENAMES_REPORTERS_MAIN}${reporter}.cpp ) +endforeach() + +# sources to combine with 200-Rpt-CatchMain{Reporter}.cpp: + +set( SOURCES_REPORTERS_TESTS + 207-Rpt-TeamCityReporter.cpp +) + # check if all sources are listed, warn if not: set( SOURCES_ALL @@ -52,6 +73,8 @@ set( SOURCES_ALL ${SOURCES_SINGLE_FILE} ${SOURCES_IDIOMATIC_MAIN} ${SOURCES_IDIOMATIC_TESTS} + ${SOURCES_REPORTERS_MAIN} + ${SOURCES_REPORTERS_TESTS} ) foreach( name ${SOURCES_ALL} ) @@ -64,16 +87,31 @@ CheckFileList( SOURCES_ALL_PATH ${EXAMPLES_DIR} ) string( REPLACE ".cpp" "" BASENAMES_SINGLE_FILE "${SOURCES_SINGLE_FILE}" ) string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_TESTS "${SOURCES_IDIOMATIC_TESTS}" ) +string( REPLACE ".cpp" "" BASENAMES_REPORTERS_TESTS "${SOURCES_REPORTERS_TESTS}" ) +string( REPLACE ".cpp" "" BASENAMES_REPORTERS_MAIN "${SOURCES_REPORTERS_MAIN}" ) set( TARGETS_SINGLE_FILE ${BASENAMES_SINGLE_FILE} ) set( TARGETS_IDIOMATIC_TESTS ${BASENAMES_IDIOMATIC_TESTS} ) -set( TARGETS_ALL ${TARGETS_SINGLE_FILE} ${TARGETS_IDIOMATIC_TESTS} 020-TestCase CatchMain ) +set( TARGETS_REPORTERS_TESTS ${BASENAMES_REPORTERS_TESTS} ) +set( TARGETS_REPORTERS_MAIN ${BASENAMES_REPORTERS_MAIN} ) + +set( TARGETS_ALL + ${TARGETS_SINGLE_FILE} + 020-TestCase + ${TARGETS_IDIOMATIC_TESTS} CatchMain + ${TARGETS_REPORTERS_TESTS} CatchMainTeamCity +) # define program targets: -add_library( CatchMain OBJECT ${EXAMPLES_DIR}/${SOURCES_IDIOMATIC_MAIN} ${HEADER_DIR}/catch2/catch.hpp ) +add_library( CatchMain OBJECT ${EXAMPLES_DIR}/${SOURCES_IDIOMATIC_MAIN} ${HEADER_DIR}/catch2/catch.hpp ) +#add_library( CatchMainAutomake OBJECT ${EXAMPLES_DIR}/200-Rpt-CatchMain.cpp ${HEADER_DIR}/catch2/catch.hpp ) +#add_library( CatchMainTap OBJECT ${EXAMPLES_DIR}/200-Rpt-CatchMain.cpp ${HEADER_DIR}/catch2/catch.hpp ) +add_library( CatchMainTeamCity OBJECT ${EXAMPLES_DIR}/200-Rpt-CatchMain.cpp ${HEADER_DIR}/catch2/catch.hpp ) -add_executable( 020-TestCase ${EXAMPLES_DIR}/020-TestCase-1.cpp ${EXAMPLES_DIR}/020-TestCase-2.cpp ${HEADER_DIR}/catch2/catch.hpp ) +#target_compile_definitions( CatchMainAutomake PRIVATE CATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_automake.hpp\" ) +#target_compile_definitions( CatchMainTap PRIVATE CATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_tap.hpp\" ) +target_compile_definitions( CatchMainTeamCity PRIVATE CATCH_EXAMPLE_RPT_1=\"include/reporters/catch_reporter_teamcity.hpp\" ) foreach( name ${TARGETS_SINGLE_FILE} ) add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp ${HEADER_DIR}/catch2/catch.hpp ) @@ -83,8 +121,18 @@ foreach( name ${TARGETS_IDIOMATIC_TESTS} ) add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp $ ${HEADER_DIR}/catch2/catch.hpp ) endforeach() +add_executable( 020-TestCase ${EXAMPLES_DIR}/020-TestCase-1.cpp ${EXAMPLES_DIR}/020-TestCase-2.cpp ${HEADER_DIR}/catch2/catch.hpp ) + +#add_executable( 207-Rpt-AutomakeReporter ${EXAMPLES_DIR}/207-Rpt-AutomakeReporter.cpp $ ${HEADER_DIR}/catch2/catch.hpp ) +#add_executable( 207-Rpt-TapReporter ${EXAMPLES_DIR}/207-Rpt-TapReporter.cpp $ ${HEADER_DIR}/catch2/catch.hpp ) +add_executable( 207-Rpt-TeamCityReporter ${EXAMPLES_DIR}/207-Rpt-TeamCityReporter.cpp $ ${HEADER_DIR}/catch2/catch.hpp ) + +#foreach( name ${TARGETS_REPORTERS_TESTS} ) +# add_executable( ${name} ${EXAMPLES_DIR}/${name}.cpp $ ${HEADER_DIR}/catch2/catch.hpp ) +#endforeach() + foreach( name ${TARGETS_ALL} ) - target_include_directories( ${name} PRIVATE ${HEADER_DIR} ) + target_include_directories( ${name} PRIVATE ${HEADER_DIR} ${CATCH_DIR} ) set_property(TARGET ${name} PROPERTY CXX_STANDARD 11) set_property(TARGET ${name} PROPERTY CXX_EXTENSIONS OFF) @@ -102,3 +150,4 @@ foreach( name ${TARGETS_ALL} ) target_compile_options( ${name} PRIVATE /W4 /w44265 /WX ) endif() endforeach() +