From 85b129c741d7493df178c4e0644cd221ad434141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Fri, 21 Jun 2019 23:15:08 +0200 Subject: [PATCH] Pipe --list* through reporters This allows us to provide machine-readable listings through the XMLReporter and it will also allow users to define their own listing format that does whatever their own tools need. --- .../internal/catch_interfaces_reporter.cpp | 86 ++++++++++++++++ include/internal/catch_interfaces_reporter.h | 9 ++ include/internal/catch_list.cpp | 99 ++++--------------- include/internal/catch_list.h | 22 ++++- include/internal/catch_session.cpp | 21 ++-- 5 files changed, 149 insertions(+), 88 deletions(-) diff --git a/include/internal/catch_interfaces_reporter.cpp b/include/internal/catch_interfaces_reporter.cpp index 0c367c5b..664dd3eb 100644 --- a/include/internal/catch_interfaces_reporter.cpp +++ b/include/internal/catch_interfaces_reporter.cpp @@ -6,7 +6,13 @@ */ #include "catch_interfaces_reporter.h" +#include "catch_console_colour.h" #include "../reporters/catch_reporter_listening.h" +#include "catch_list.h" +#include "catch_text.h" + +#include +#include namespace Catch { @@ -108,6 +114,86 @@ namespace Catch { void IStreamingReporter::fatalErrorEncountered( StringRef ) {} bool IStreamingReporter::isMulti() const { return false; } + void IStreamingReporter::listReporters(std::vector const& descriptions, Config const& config) { + Catch::cout() << "Available reporters:\n"; + const auto maxNameLen = std::max_element(descriptions.begin(), descriptions.end(), + [](ReporterDescription const& lhs, ReporterDescription const& rhs) { return lhs.name.size() < rhs.name.size(); }) + ->name.size(); + + for (auto const& desc : descriptions) { + if (config.verbosity() == Verbosity::Quiet) { + Catch::cout() + << Column(desc.name) + .indent(2) + .width(5 + maxNameLen) << '\n'; + } else { + Catch::cout() + << Column(desc.name + ":") + .indent(2) + .width(5 + maxNameLen) + + Column(desc.description) + .initialIndent(0) + .indent(2) + .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) + << '\n'; + } + } + Catch::cout() << std::endl; + } + + void IStreamingReporter::listTests(std::vector const& tests, Config const& config) { + if (config.hasTestFilters()) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } + + for (auto const& testCaseInfo : tests) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard(colour); + + Catch::cout() << Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n'; + if (config.verbosity() >= Verbosity::High) { + Catch::cout() << Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if (description.empty()) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column(description).indent(4) << std::endl; + } + if (!testCaseInfo.tags.empty() && config.verbosity() > Verbosity::Quiet) { + Catch::cout() << Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; + } + } + + if (!config.hasTestFilters()) { + Catch::cout() << pluralise(tests.size(), "test case") << '\n' << std::endl; + } else { + Catch::cout() << pluralise(tests.size(), "matching test case") << '\n' << std::endl; + } + } + + void IStreamingReporter::listTags(std::vector const& tags, Config const& config) { + if (config.hasTestFilters()) { + Catch::cout() << "Tags for matching test cases:\n"; + } else { + Catch::cout() << "All available tags:\n"; + } + + for (auto const& tagCount : tags) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.count << " "; + auto str = rss.str(); + auto wrapper = Column(tagCount.all()) + .initialIndent(0) + .indent(str.size()) + .width(CATCH_CONFIG_CONSOLE_WIDTH - 10); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise(tags.size(), "tag") << '\n' << std::endl; + } + IReporterFactory::~IReporterFactory() = default; IReporterRegistry::~IReporterRegistry() = default; diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index 9c2b0267..d73cf025 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -33,6 +33,9 @@ namespace Catch { + struct ReporterDescription; + struct TagInfo; + struct ReporterConfig { explicit ReporterConfig( IConfigPtr const& _fullConfig ); @@ -244,6 +247,12 @@ namespace Catch { virtual void fatalErrorEncountered( StringRef name ); virtual bool isMulti() const; + + // Listing support + virtual void listReporters(std::vector const& descriptions, Config const& config); + virtual void listTests(std::vector const& tests, Config const& config); + virtual void listTags(std::vector const& tags, Config const& config); + }; using IStreamingReporterPtr = std::unique_ptr; diff --git a/include/internal/catch_list.cpp b/include/internal/catch_list.cpp index 7a1ae7d9..c93a8410 100644 --- a/include/internal/catch_list.cpp +++ b/include/internal/catch_list.cpp @@ -23,65 +23,22 @@ #include #include -#include #include namespace Catch { namespace { - struct TagInfo { - void add(std::string const& spelling); - std::string all() const; - - std::set spellings; - std::size_t count = 0; - }; - - - void listTests(Config const& config) { + void listTests(IStreamingReporter& reporter, Config const& config) { TestSpec testSpec = config.testSpec(); - if (config.hasTestFilters()) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - } - auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (auto const& testCaseInfo : matchedTestCases) { - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard(colour); - - Catch::cout() << Column(testCaseInfo.name).initialIndent(2).indent(4) << "\n"; - if (config.verbosity() >= Verbosity::High) { - Catch::cout() << Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl; - std::string description = testCaseInfo.description; - if (description.empty()) - description = "(NO DESCRIPTION)"; - Catch::cout() << Column(description).indent(4) << std::endl; - } - if (!testCaseInfo.tags.empty()) - Catch::cout() << Column(testCaseInfo.tagsAsString()).indent(6) << "\n"; - } - - if (!config.hasTestFilters()) - Catch::cout() << pluralise(matchedTestCases.size(), "test case") << '\n' << std::endl; - else - Catch::cout() << pluralise(matchedTestCases.size(), "matching test case") << '\n' << std::endl; + reporter.listTests(matchedTestCases, config); } - void listTags(Config const& config) { + void listTags(IStreamingReporter& reporter, Config const& config) { TestSpec testSpec = config.testSpec(); - if (config.hasTestFilters()) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - } + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); std::map tagCounts; - - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); for (auto const& testCase : matchedTestCases) { for (auto const& tagName : testCase.getTestCaseInfo().tags) { std::string lcaseTagName = toLower(tagName); @@ -92,38 +49,24 @@ namespace Catch { } } - for (auto const& tagCount : tagCounts) { - ReusableStringStream rss; - rss << " " << std::setw(2) << tagCount.second.count << " "; - auto str = rss.str(); - auto wrapper = Column(tagCount.second.all()) - .initialIndent(0) - .indent(str.size()) - .width(CATCH_CONFIG_CONSOLE_WIDTH - 10); - Catch::cout() << str << wrapper << '\n'; + std::vector infos; infos.reserve(tagCounts.size()); + for (auto const& tagc : tagCounts) { + infos.push_back(std::move(tagc.second)); } - Catch::cout() << pluralise(tagCounts.size(), "tag") << '\n' << std::endl; + + reporter.listTags(infos, config); } - void listReporters() { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - std::size_t maxNameLen = 0; - for (auto const& factoryKvp : factories) - maxNameLen = (std::max)(maxNameLen, factoryKvp.first.size()); + void listReporters(IStreamingReporter& reporter, Config const& config) { + std::vector descriptions; - for (auto const& factoryKvp : factories) { - Catch::cout() - << Column(factoryKvp.first + ":") - .indent(2) - .width(5 + maxNameLen) - + Column(factoryKvp.second->getDescription()) - .initialIndent(0) - .indent(2) - .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) - << "\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + descriptions.reserve(factories.size()); + for (auto const& fac : factories) { + descriptions.push_back({ fac.first, fac.second->getDescription() }); } - Catch::cout() << std::endl; + + reporter.listReporters(descriptions, config); } } // end anonymous namespace @@ -149,20 +92,20 @@ namespace Catch { return out; } - bool list( std::shared_ptr const& config ) { + bool list( IStreamingReporter& reporter, std::shared_ptr const& config ) { bool listed = false; getCurrentMutableContext().setConfig( config ); if (config->listTests()) { listed = true; - listTests(*config); + listTests(reporter, *config); } if (config->listTags()) { listed = true; - listTags(*config); + listTags(reporter, *config); } if (config->listReporters()) { listed = true; - listReporters(); + listReporters(reporter, *config); } return listed; } diff --git a/include/internal/catch_list.h b/include/internal/catch_list.h index 70567b11..eff8b09e 100644 --- a/include/internal/catch_list.h +++ b/include/internal/catch_list.h @@ -10,9 +10,29 @@ #include "catch_config.hpp" +#include +#include + + namespace Catch { - bool list( std::shared_ptr const& config ); + struct IStreamingReporter; + + struct ReporterDescription { + std::string name, description; + }; + + struct TagInfo { + void add(std::string const& spelling); + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; + + struct testClassInfo {}; + + bool list( IStreamingReporter& reporter, std::shared_ptr const& config ); } // end namespace Catch diff --git a/include/internal/catch_session.cpp b/include/internal/catch_session.cpp index f490c88a..dd3dc241 100644 --- a/include/internal/catch_session.cpp +++ b/include/internal/catch_session.cpp @@ -62,14 +62,14 @@ namespace Catch { class TestGroup { public: - explicit TestGroup(std::shared_ptr const& config) + explicit TestGroup(IStreamingReporterPtr&& reporter, std::shared_ptr const& config) : m_config{config} - , m_context{config, makeReporter(config)} + , m_context{config, std::move(reporter)} { auto const& allTestCases = getAllTestCasesSorted(*m_config); m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); auto const& invalidArgs = m_config->testSpec().getInvalidArgs(); - + if (m_matches.empty() && invalidArgs.empty()) { for (auto const& test : allTestCases) if (!test.isHidden()) @@ -97,12 +97,12 @@ namespace Catch { totals.error = -1; } } - + if (!invalidArgs.empty()) { - for (auto const& invalidArg: invalidArgs) + for (auto const& invalidArg: invalidArgs) m_context.reporter().reportInvalidArguments(invalidArg); - } - + } + m_context.testGroupEnded(m_config->name(), totals, 1, 1); return totals; } @@ -287,12 +287,15 @@ namespace Catch { if( m_configData.filenamesAsTags ) applyFilenamesAsTags( *m_config ); + // Create reporter(s) so we can route listings through them + auto reporter = makeReporter(m_config); + // Handle list request - if (list(m_config)) { + if (list(*reporter, m_config)) { return 0; } - TestGroup tests { m_config }; + TestGroup tests { std::move(reporter), m_config }; auto const totals = tests.execute(); if( m_config->warnAboutNoTests() && totals.error == -1 )