Reporters.tests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // Copyright Catch2 Authors
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // https://www.boost.org/LICENSE_1_0.txt)
  5. // SPDX-License-Identifier: BSL-1.0
  6. #include <catch2/catch_test_macros.hpp>
  7. #include <catch2/catch_test_case_info.hpp>
  8. #include <catch2/catch_config.hpp>
  9. #include <catch2/interfaces/catch_interfaces_reporter.hpp>
  10. #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
  11. #include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
  12. #include <catch2/internal/catch_console_colour.hpp>
  13. #include <catch2/internal/catch_enforce.hpp>
  14. #include <catch2/internal/catch_list.hpp>
  15. #include <catch2/internal/catch_reporter_registry.hpp>
  16. #include <catch2/internal/catch_istream.hpp>
  17. #include <catch2/matchers/catch_matchers_string.hpp>
  18. #include <catch2/reporters/catch_reporter_helpers.hpp>
  19. #include <catch2/reporters/catch_reporter_event_listener.hpp>
  20. #include <catch2/reporters/catch_reporter_streaming_base.hpp>
  21. #include <catch2/reporters/catch_reporter_multi.hpp>
  22. #include <catch2/internal/catch_move_and_forward.hpp>
  23. #include <sstream>
  24. namespace {
  25. class StringIStream : public Catch::IStream {
  26. public:
  27. std::ostream& stream() override { return sstr; }
  28. std::string str() const { return sstr.str(); }
  29. private:
  30. std::stringstream sstr;
  31. };
  32. //! config must outlive the function
  33. Catch::ReporterConfig makeDummyRepConfig( Catch::Config const& config ) {
  34. return Catch::ReporterConfig{
  35. &config,
  36. Catch::Detail::make_unique<StringIStream>(),
  37. Catch::ColourMode::None,
  38. {} };
  39. }
  40. }
  41. TEST_CASE( "The default listing implementation write to provided stream",
  42. "[reporters][reporter-helpers]" ) {
  43. using Catch::Matchers::ContainsSubstring;
  44. using namespace std::string_literals;
  45. StringIStream sstream;
  46. SECTION( "Listing tags" ) {
  47. std::vector<Catch::TagInfo> tags(1);
  48. tags[0].add("fakeTag"_catch_sr);
  49. Catch::defaultListTags(sstream.stream(), tags, false);
  50. auto listingString = sstream.str();
  51. REQUIRE_THAT(listingString, ContainsSubstring("[fakeTag]"s));
  52. }
  53. SECTION( "Listing reporters" ) {
  54. std::vector<Catch::ReporterDescription> reporters(
  55. { { "fake reporter", "fake description" } } );
  56. Catch::defaultListReporters(sstream.stream(), reporters, Catch::Verbosity::Normal);
  57. auto listingString = sstream.str();
  58. REQUIRE_THAT( listingString,
  59. ContainsSubstring( "fake reporter"s ) &&
  60. ContainsSubstring( "fake description"s ) );
  61. }
  62. SECTION( "Listing tests" ) {
  63. Catch::TestCaseInfo fakeInfo{
  64. ""s,
  65. { "fake test name"_catch_sr, "[fakeTestTag]"_catch_sr },
  66. { "fake-file.cpp", 123456789 } };
  67. std::vector<Catch::TestCaseHandle> tests({ {&fakeInfo, nullptr} });
  68. auto colour = Catch::makeColourImpl( Catch::ColourMode::None, &sstream);
  69. Catch::defaultListTests(sstream.stream(), colour.get(), tests, false, Catch::Verbosity::Normal);
  70. auto listingString = sstream.str();
  71. REQUIRE_THAT( listingString,
  72. ContainsSubstring( "fake test name"s ) &&
  73. ContainsSubstring( "fakeTestTag"s ) );
  74. }
  75. SECTION( "Listing listeners" ) {
  76. std::vector<Catch::ListenerDescription> listeners(
  77. { { "fakeListener"_catch_sr, "fake description" } } );
  78. Catch::defaultListListeners( sstream.stream(), listeners );
  79. auto listingString = sstream.str();
  80. REQUIRE_THAT( listingString,
  81. ContainsSubstring( "fakeListener"s ) &&
  82. ContainsSubstring( "fake description"s ) );
  83. }
  84. }
  85. TEST_CASE( "Reporter's write listings to provided stream", "[reporters]" ) {
  86. using Catch::Matchers::ContainsSubstring;
  87. using namespace std::string_literals;
  88. auto const& factories = Catch::getRegistryHub().getReporterRegistry().getFactories();
  89. // If there are no reporters, the test would pass falsely
  90. // while there is something obviously broken
  91. REQUIRE_FALSE(factories.empty());
  92. for (auto const& factory : factories) {
  93. INFO("Tested reporter: " << factory.first);
  94. auto sstream = Catch::Detail::make_unique<StringIStream>();
  95. auto& sstreamRef = *sstream.get();
  96. Catch::Config config( Catch::ConfigData{} );
  97. auto reporter = factory.second->create( Catch::ReporterConfig{
  98. &config, CATCH_MOVE( sstream ), Catch::ColourMode::None, {} } );
  99. DYNAMIC_SECTION( factory.first << " reporter lists tags" ) {
  100. std::vector<Catch::TagInfo> tags(1);
  101. tags[0].add("fakeTag"_catch_sr);
  102. reporter->listTags(tags);
  103. auto listingString = sstreamRef.str();
  104. REQUIRE_THAT(listingString, ContainsSubstring("fakeTag"s));
  105. }
  106. DYNAMIC_SECTION( factory.first << " reporter lists reporters" ) {
  107. std::vector<Catch::ReporterDescription> reporters(
  108. { { "fake reporter", "fake description" } } );
  109. reporter->listReporters(reporters);
  110. auto listingString = sstreamRef.str();
  111. REQUIRE_THAT(listingString, ContainsSubstring("fake reporter"s));
  112. }
  113. DYNAMIC_SECTION( factory.first << " reporter lists tests" ) {
  114. Catch::TestCaseInfo fakeInfo{
  115. ""s,
  116. { "fake test name"_catch_sr, "[fakeTestTag]"_catch_sr },
  117. { "fake-file.cpp", 123456789 } };
  118. std::vector<Catch::TestCaseHandle> tests({ {&fakeInfo, nullptr} });
  119. reporter->listTests(tests);
  120. auto listingString = sstreamRef.str();
  121. REQUIRE_THAT( listingString,
  122. ContainsSubstring( "fake test name"s ) &&
  123. ContainsSubstring( "fakeTestTag"s ) );
  124. }
  125. }
  126. }
  127. TEST_CASE("Reproducer for #2309 - a very long description past 80 chars (default console width) with a late colon : blablabla", "[console-reporter]") {
  128. SUCCEED();
  129. }
  130. namespace {
  131. // A listener that writes provided string into destination,
  132. // to record order of testRunStarting invocation.
  133. class MockListener : public Catch::EventListenerBase {
  134. std::string m_witness;
  135. std::vector<std::string>& m_recorder;
  136. public:
  137. MockListener( std::string witness,
  138. std::vector<std::string>& recorder,
  139. Catch::IConfig const* config ):
  140. EventListenerBase( config ),
  141. m_witness( witness ),
  142. m_recorder( recorder )
  143. {}
  144. void testRunStarting( Catch::TestRunInfo const& ) override {
  145. m_recorder.push_back( m_witness );
  146. }
  147. };
  148. // A reporter that writes provided string into destination,
  149. // to record order of testRunStarting invocation.
  150. class MockReporter : public Catch::StreamingReporterBase {
  151. std::string m_witness;
  152. std::vector<std::string>& m_recorder;
  153. public:
  154. MockReporter( std::string witness,
  155. std::vector<std::string>& recorder,
  156. Catch::ReporterConfig&& config ):
  157. StreamingReporterBase( CATCH_MOVE(config) ),
  158. m_witness( witness ),
  159. m_recorder( recorder )
  160. {}
  161. void testRunStarting( Catch::TestRunInfo const& ) override {
  162. m_recorder.push_back( m_witness );
  163. }
  164. };
  165. } // namespace
  166. TEST_CASE("Multireporter calls reporters and listeners in correct order",
  167. "[reporters][multi-reporter]") {
  168. Catch::Config config( Catch::ConfigData{} );
  169. // We add reporters before listeners, to check that internally they
  170. // get sorted properly, and listeners are called first anyway.
  171. Catch::MultiReporter multiReporter( &config );
  172. std::vector<std::string> records;
  173. multiReporter.addReporter( Catch::Detail::make_unique<MockReporter>(
  174. "Goodbye", records, makeDummyRepConfig(config) ) );
  175. multiReporter.addListener(
  176. Catch::Detail::make_unique<MockListener>( "Hello", records, &config ) );
  177. multiReporter.addListener(
  178. Catch::Detail::make_unique<MockListener>( "world", records, &config ) );
  179. multiReporter.addReporter( Catch::Detail::make_unique<MockReporter>(
  180. "world", records, makeDummyRepConfig(config) ) );
  181. multiReporter.testRunStarting( { "" } );
  182. std::vector<std::string> expected( { "Hello", "world", "Goodbye", "world" } );
  183. REQUIRE( records == expected );
  184. }
  185. namespace {
  186. // A listener that sets it preferences to test that multireporter,
  187. // properly sets up its own preferences
  188. class PreferenceListener : public Catch::EventListenerBase {
  189. public:
  190. PreferenceListener( bool redirectStdout,
  191. bool reportAllAssertions,
  192. Catch::IConfig const* config ):
  193. EventListenerBase( config ) {
  194. m_preferences.shouldRedirectStdOut = redirectStdout;
  195. m_preferences.shouldReportAllAssertions = reportAllAssertions;
  196. }
  197. };
  198. // A reporter that sets it preferences to test that multireporter,
  199. // properly sets up its own preferences
  200. class PreferenceReporter : public Catch::StreamingReporterBase {
  201. public:
  202. PreferenceReporter( bool redirectStdout,
  203. bool reportAllAssertions,
  204. Catch::ReporterConfig&& config ):
  205. StreamingReporterBase( CATCH_MOVE(config) ) {
  206. m_preferences.shouldRedirectStdOut = redirectStdout;
  207. m_preferences.shouldReportAllAssertions = reportAllAssertions;
  208. }
  209. };
  210. } // namespace
  211. TEST_CASE("Multireporter updates ReporterPreferences properly",
  212. "[reporters][multi-reporter]") {
  213. Catch::Config config( Catch::ConfigData{} );
  214. Catch::MultiReporter multiReporter( &config );
  215. // Post init defaults
  216. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == false );
  217. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == false );
  218. SECTION( "Adding listeners" ) {
  219. multiReporter.addListener(
  220. Catch::Detail::make_unique<PreferenceListener>(
  221. true, false, &config ) );
  222. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true );
  223. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == false );
  224. multiReporter.addListener(
  225. Catch::Detail::make_unique<PreferenceListener>(
  226. false, true, &config ) );
  227. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true );
  228. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true);
  229. multiReporter.addListener(
  230. Catch::Detail::make_unique<PreferenceListener>(
  231. false, false, &config ) );
  232. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true );
  233. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true );
  234. }
  235. SECTION( "Adding reporters" ) {
  236. multiReporter.addReporter(
  237. Catch::Detail::make_unique<PreferenceReporter>(
  238. true, false, makeDummyRepConfig(config) ) );
  239. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true );
  240. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == false );
  241. multiReporter.addReporter(
  242. Catch::Detail::make_unique<PreferenceReporter>(
  243. false, true, makeDummyRepConfig( config ) ) );
  244. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true );
  245. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true );
  246. multiReporter.addReporter(
  247. Catch::Detail::make_unique<PreferenceReporter>(
  248. false, false, makeDummyRepConfig( config ) ) );
  249. REQUIRE( multiReporter.getPreferences().shouldRedirectStdOut == true );
  250. REQUIRE( multiReporter.getPreferences().shouldReportAllAssertions == true );
  251. }
  252. }
  253. namespace {
  254. class TestReporterFactory : public Catch::IReporterFactory {
  255. Catch::IEventListenerPtr create( Catch::ReporterConfig&& ) const override {
  256. CATCH_INTERNAL_ERROR(
  257. "This factory should never create a reporter" );
  258. }
  259. std::string getDescription() const override {
  260. return "Fake test factory";
  261. }
  262. };
  263. }
  264. TEST_CASE("Registering reporter with '::' in name fails",
  265. "[reporters][registration]") {
  266. Catch::ReporterRegistry registry;
  267. REQUIRE_THROWS_WITH( registry.registerReporter(
  268. "with::doublecolons",
  269. Catch::Detail::make_unique<TestReporterFactory>() ),
  270. "'::' is not allowed in reporter name: 'with::doublecolons'" );
  271. }
  272. TEST_CASE("Registering multiple reporters with the same name fails",
  273. "[reporters][registration][approvals]") {
  274. Catch::ReporterRegistry registry;
  275. registry.registerReporter(
  276. "some-reporter-name",
  277. Catch::Detail::make_unique<TestReporterFactory>() );
  278. REQUIRE_THROWS_WITH(
  279. registry.registerReporter(
  280. "some-reporter-name",
  281. Catch::Detail::make_unique<TestReporterFactory>() ),
  282. "reporter using 'some-reporter-name' as name was already registered" );
  283. }