// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { class StringIStream : public Catch::IStream { public: std::ostream& stream() override { return sstr; } std::string str() const { return sstr.str(); } private: std::stringstream sstr; }; //! config must outlive the function Catch::ReporterConfig makeDummyRepConfig( Catch::Config const& config ) { return Catch::ReporterConfig{ &config, Catch::Detail::make_unique(), Catch::ColourMode::None, {} }; } } TEST_CASE( "The default listing implementation write to provided stream", "[reporters][reporter-helpers]" ) { using Catch::Matchers::ContainsSubstring; using namespace std::string_literals; StringIStream sstream; SECTION( "Listing tags" ) { std::vector tags(1); tags[0].add("fakeTag"_catch_sr); Catch::defaultListTags(sstream.stream(), tags, false); auto listingString = sstream.str(); REQUIRE_THAT(listingString, ContainsSubstring("[fakeTag]"s)); } SECTION( "Listing reporters" ) { std::vector reporters( { { "fake reporter", "fake description" } } ); Catch::defaultListReporters(sstream.stream(), reporters, Catch::Verbosity::Normal); auto listingString = sstream.str(); REQUIRE_THAT( listingString, ContainsSubstring( "fake reporter"s ) && ContainsSubstring( "fake description"s ) ); } SECTION( "Listing tests" ) { Catch::TestCaseInfo fakeInfo{ ""s, { "fake test name"_catch_sr, "[fakeTestTag]"_catch_sr }, { "fake-file.cpp", 123456789 } }; std::vector tests({ {&fakeInfo, nullptr} }); auto colour = Catch::makeColourImpl( Catch::ColourMode::None, &sstream); Catch::defaultListTests(sstream.stream(), colour.get(), tests, false, Catch::Verbosity::Normal); auto listingString = sstream.str(); REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) ); } SECTION( "Listing listeners" ) { std::vector listeners( { { "fakeListener"_catch_sr, "fake description" } } ); Catch::defaultListListeners( sstream.stream(), listeners ); auto listingString = sstream.str(); REQUIRE_THAT( listingString, ContainsSubstring( "fakeListener"s ) && ContainsSubstring( "fake description"s ) ); } } TEST_CASE( "Reporter's write listings to provided stream", "[reporters]" ) { using Catch::Matchers::ContainsSubstring; using namespace std::string_literals; auto const& factories = Catch::getRegistryHub().getReporterRegistry().getFactories(); // If there are no reporters, the test would pass falsely // while there is something obviously broken REQUIRE_FALSE(factories.empty()); for (auto const& factory : factories) { INFO("Tested reporter: " << factory.first); auto sstream = Catch::Detail::make_unique(); auto& sstreamRef = *sstream.get(); Catch::Config config( Catch::ConfigData{} ); auto reporter = factory.second->create( Catch::ReporterConfig{ &config, CATCH_MOVE( sstream ), Catch::ColourMode::None, {} } ); DYNAMIC_SECTION( factory.first << " reporter lists tags" ) { std::vector tags(1); tags[0].add("fakeTag"_catch_sr); reporter->listTags(tags); auto listingString = sstreamRef.str(); REQUIRE_THAT(listingString, ContainsSubstring("fakeTag"s)); } DYNAMIC_SECTION( factory.first << " reporter lists reporters" ) { std::vector reporters( { { "fake reporter", "fake description" } } ); reporter->listReporters(reporters); auto listingString = sstreamRef.str(); REQUIRE_THAT(listingString, ContainsSubstring("fake reporter"s)); } DYNAMIC_SECTION( factory.first << " reporter lists tests" ) { Catch::TestCaseInfo fakeInfo{ ""s, { "fake test name"_catch_sr, "[fakeTestTag]"_catch_sr }, { "fake-file.cpp", 123456789 } }; std::vector tests({ {&fakeInfo, nullptr} }); reporter->listTests(tests); auto listingString = sstreamRef.str(); REQUIRE_THAT( listingString, ContainsSubstring( "fake test name"s ) && ContainsSubstring( "fakeTestTag"s ) ); } } } TEST_CASE("Reproducer for #2309 - a very long description past 80 chars (default console width) with a late colon : blablabla", "[console-reporter]") { SUCCEED(); } namespace { // A listener that writes provided string into destination, // to record order of testRunStarting invocation. class MockListener : public Catch::EventListenerBase { std::string m_witness; std::vector& m_recorder; public: MockListener( std::string witness, std::vector& recorder, Catch::IConfig const* config ): EventListenerBase( config ), m_witness( witness ), m_recorder( recorder ) {} void testRunStarting( Catch::TestRunInfo const& ) override { m_recorder.push_back( m_witness ); } }; // A reporter that writes provided string into destination, // to record order of testRunStarting invocation. class MockReporter : public Catch::StreamingReporterBase { std::string m_witness; std::vector& m_recorder; public: MockReporter( std::string witness, std::vector& recorder, Catch::ReporterConfig&& config ): StreamingReporterBase( CATCH_MOVE(config) ), m_witness( witness ), m_recorder( recorder ) {} void testRunStarting( Catch::TestRunInfo const& ) override { m_recorder.push_back( m_witness ); } }; } // namespace TEST_CASE("Multireporter calls reporters and listeners in correct order", "[reporters][multi-reporter]") { Catch::Config config( Catch::ConfigData{} ); // We add reporters before listeners, to check that internally they // get sorted properly, and listeners are called first anyway. Catch::MultiReporter multiReporter( &config ); std::vector records; multiReporter.addReporter( Catch::Detail::make_unique( "Goodbye", records, makeDummyRepConfig(config) ) ); multiReporter.addListener( Catch::Detail::make_unique( "Hello", records, &config ) ); multiReporter.addListener( Catch::Detail::make_unique( "world", records, &config ) ); multiReporter.addReporter( Catch::Detail::make_unique( "world", records, makeDummyRepConfig(config) ) ); multiReporter.testRunStarting( { "" } ); std::vector expected( { "Hello", "world", "Goodbye", "world" } ); REQUIRE( records == expected ); } namespace { // A listener that sets it preferences to test that multireporter, // properly sets up its own preferences class PreferenceListener : public Catch::EventListenerBase { public: PreferenceListener( bool redirectStdout, bool reportAllAssertions, Catch::IConfig const* config ): EventListenerBase( config ) { m_preferences.shouldRedirectStdOut = redirectStdout; m_preferences.shouldReportAllAssertions = reportAllAssertions; } }; // A reporter that sets it preferences to test that multireporter, // properly sets up its own preferences class PreferenceReporter : public Catch::StreamingReporterBase { public: PreferenceReporter( bool redirectStdout, bool reportAllAssertions, Catch::ReporterConfig&& config ): StreamingReporterBase( CATCH_MOVE(config) ) { m_preferences.shouldRedirectStdOut = redirectStdout; m_preferences.shouldReportAllAssertions = reportAllAssertions; } }; } // namespace TEST_CASE("Multireporter updates ReporterPreferences properly", "[reporters][multi-reporter]") { Catch::Config config( Catch::ConfigData{} ); Catch::MultiReporter multiReporter( &config ); // Post init defaults REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == false ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == false ); SECTION( "Adding listeners" ) { multiReporter.addListener( Catch::Detail::make_unique( true, false, &config ) ); REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == false ); multiReporter.addListener( Catch::Detail::make_unique( false, true, &config ) ); REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true); multiReporter.addListener( Catch::Detail::make_unique( false, false, &config ) ); REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true ); } SECTION( "Adding reporters" ) { multiReporter.addReporter( Catch::Detail::make_unique( true, false, makeDummyRepConfig(config) ) ); REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == false ); multiReporter.addReporter( Catch::Detail::make_unique( false, true, makeDummyRepConfig( config ) ) ); REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true ); multiReporter.addReporter( Catch::Detail::make_unique( false, false, makeDummyRepConfig( config ) ) ); REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true ); REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true ); } } namespace { class TestReporterFactory : public Catch::IReporterFactory { Catch::IEventListenerPtr create( Catch::ReporterConfig&& ) const override { CATCH_INTERNAL_ERROR( "This factory should never create a reporter" ); } std::string getDescription() const override { return "Fake test factory"; } }; } TEST_CASE("Registering reporter with '::' in name fails", "[reporters][registration]") { Catch::ReporterRegistry registry; REQUIRE_THROWS_WITH( registry.registerReporter( "with::doublecolons", Catch::Detail::make_unique() ), "'::' is not allowed in reporter name: 'with::doublecolons'" ); } TEST_CASE("Registering multiple reporters with the same name fails", "[reporters][registration][approvals]") { Catch::ReporterRegistry registry; registry.registerReporter( "some-reporter-name", Catch::Detail::make_unique() ); REQUIRE_THROWS_WITH( registry.registerReporter( "some-reporter-name", Catch::Detail::make_unique() ), "reporter using 'some-reporter-name' as name was already registered" ); }