Extract the default listing implementation out of reporter interface

Also generalized the implementations to write to the provided
output stream, which will be required for the follow up changes,
where the listings should happen to the location user asked for
by specifying the `-o` flag.
This commit is contained in:
Martin Hořeňovský 2021-01-25 12:22:54 +01:00
parent 7111b2a8e2
commit 6798c139a6
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
3 changed files with 170 additions and 97 deletions

View File

@ -11,34 +11,15 @@
#include <catch2/internal/catch_console_width.hpp> #include <catch2/internal/catch_console_width.hpp>
#include <catch2/catch_message.hpp> #include <catch2/catch_message.hpp>
#include <catch2/internal/catch_list.hpp> #include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_string_manip.hpp> #include <catch2/internal/catch_string_manip.hpp>
#include <catch2/catch_test_case_info.hpp> #include <catch2/catch_test_case_info.hpp>
#include <catch2/internal/catch_textflow.hpp> #include <catch2/reporters/catch_reporter_helpers.hpp>
#include <algorithm> #include <algorithm>
#include <iomanip> #include <iomanip>
namespace Catch { namespace Catch {
namespace {
void listTestNamesOnly( std::vector<TestCaseHandle> const& tests ) {
for ( auto const& test : tests ) {
auto const& testCaseInfo = test.getTestCaseInfo();
if ( startsWith( testCaseInfo.name, '#' ) ) {
Catch::cout() << '"' << testCaseInfo.name << '"';
} else {
Catch::cout() << testCaseInfo.name;
}
Catch::cout() << '\n';
}
Catch::cout() << std::flush;
}
} // end unnamed namespace
ReporterConfig::ReporterConfig( IConfig const* _fullConfig ) ReporterConfig::ReporterConfig( IConfig const* _fullConfig )
: m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
@ -128,89 +109,19 @@ namespace Catch {
void IStreamingReporter::fatalErrorEncountered( StringRef ) {} void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
void IStreamingReporter::listReporters(std::vector<ReporterDescription> const& descriptions) { void IStreamingReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
Catch::cout() << "Available reporters:\n"; defaultListReporters(
const auto maxNameLen = std::max_element(descriptions.begin(), descriptions.end(), Catch::cout(), descriptions, m_config->verbosity() );
[](ReporterDescription const& lhs, ReporterDescription const& rhs) { return lhs.name.size() < rhs.name.size(); })
->name.size();
for (auto const& desc : descriptions) {
if (m_config->verbosity() == Verbosity::Quiet) {
Catch::cout()
<< TextFlow::Column(desc.name)
.indent(2)
.width(5 + maxNameLen) << '\n';
} else {
Catch::cout()
<< TextFlow::Column(desc.name + ":")
.indent(2)
.width(5 + maxNameLen)
+ TextFlow::Column(desc.description)
.initialIndent(0)
.indent(2)
.width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8)
<< '\n';
}
}
Catch::cout() << std::endl;
} }
void IStreamingReporter::listTests(std::vector<TestCaseHandle> const& tests) { void IStreamingReporter::listTests(std::vector<TestCaseHandle> const& tests) {
// We special case this to provide the equivalent of old defaultListTests( Catch::cout(),
// `--list-test-names-only`, which could then be used by the tests,
// `--input-file` option. m_config->hasTestFilters(),
if (m_config->verbosity() == Verbosity::Quiet) { m_config->verbosity() );
listTestNamesOnly(tests);
return;
}
if (m_config->hasTestFilters()) {
Catch::cout() << "Matching test cases:\n";
} else {
Catch::cout() << "All available test cases:\n";
}
for (auto const& test : tests) {
auto const& testCaseInfo = test.getTestCaseInfo();
Colour::Code colour = testCaseInfo.isHidden()
? Colour::SecondaryText
: Colour::None;
Colour colourGuard(colour);
Catch::cout() << TextFlow::Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n';
if (m_config->verbosity() >= Verbosity::High) {
Catch::cout() << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl;
}
if ( !testCaseInfo.tags.empty() &&
m_config->verbosity() > Verbosity::Quiet ) {
Catch::cout() << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
}
}
if (m_config->hasTestFilters()) {
Catch::cout() << pluralise(tests.size(), "matching test case") << '\n' << std::endl;
} else {
Catch::cout() << pluralise(tests.size(), "test case") << '\n' << std::endl;
}
} }
void IStreamingReporter::listTags(std::vector<TagInfo> const& tags) { void IStreamingReporter::listTags(std::vector<TagInfo> const& tags) {
if (m_config->hasTestFilters()) { defaultListTags( Catch::cout(), tags, m_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 = TextFlow::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;
} }
} // end namespace Catch } // end namespace Catch

View File

@ -21,13 +21,40 @@
#include <catch2/interfaces/catch_interfaces_config.hpp> #include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_console_width.hpp> #include <catch2/internal/catch_console_width.hpp>
#include <catch2/internal/catch_errno_guard.hpp> #include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_stream.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_console_colour.hpp>
#include <catch2/catch_tostring.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <algorithm>
#include <cfloat> #include <cfloat>
#include <cstdio> #include <cstdio>
#include <ostream> #include <ostream>
#include <iomanip>
namespace Catch { namespace Catch {
namespace {
void listTestNamesOnly(std::ostream& out,
std::vector<TestCaseHandle> const& tests) {
for (auto const& test : tests) {
auto const& testCaseInfo = test.getTestCaseInfo();
if (startsWith(testCaseInfo.name, '#')) {
out << '"' << testCaseInfo.name << '"';
} else {
out << testCaseInfo.name;
}
out << '\n';
}
out << std::flush;
}
} // end unnamed namespace
// Because formatting using c++ streams is stateful, drop down to C is // Because formatting using c++ streams is stateful, drop down to C is
// required Alternatively we could use stringstream, but its performance // required Alternatively we could use stringstream, but its performance
// is... not good. // is... not good.
@ -89,6 +116,101 @@ namespace Catch {
return out; return out;
} }
void
defaultListReporters( std::ostream& out,
std::vector<ReporterDescription> const& descriptions,
Verbosity verbosity ) {
out << "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 ( verbosity == Verbosity::Quiet ) {
out << TextFlow::Column( desc.name )
.indent( 2 )
.width( 5 + maxNameLen )
<< '\n';
} else {
out << TextFlow::Column( desc.name + ":" )
.indent( 2 )
.width( 5 + maxNameLen ) +
TextFlow::Column( desc.description )
.initialIndent( 0 )
.indent( 2 )
.width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
<< '\n';
}
}
out << '\n' << std::flush;
}
void defaultListTags( std::ostream& out,
std::vector<TagInfo> const& tags,
bool isFiltered ) {
if ( isFiltered ) {
out << "Tags for matching test cases:\n";
} else {
out << "All available tags:\n";
}
for ( auto const& tagCount : tags ) {
ReusableStringStream rss;
rss << " " << std::setw( 2 ) << tagCount.count << " ";
auto str = rss.str();
auto wrapper = TextFlow::Column( tagCount.all() )
.initialIndent( 0 )
.indent( str.size() )
.width( CATCH_CONFIG_CONSOLE_WIDTH - 10 );
out << str << wrapper << '\n';
}
out << pluralise( tags.size(), "tag" ) << '\n' << std::endl;
}
void defaultListTests(std::ostream& out, std::vector<TestCaseHandle> const& tests, bool isFiltered, Verbosity verbosity) {
// We special case this to provide the equivalent of old
// `--list-test-names-only`, which could then be used by the
// `--input-file` option.
if (verbosity == Verbosity::Quiet) {
listTestNamesOnly(out, tests);
return;
}
if (isFiltered) {
out << "Matching test cases:\n";
} else {
out << "All available test cases:\n";
}
for (auto const& test : tests) {
auto const& testCaseInfo = test.getTestCaseInfo();
Colour::Code colour = testCaseInfo.isHidden()
? Colour::SecondaryText
: Colour::None;
Colour colourGuard(colour);
out << TextFlow::Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n';
if (verbosity >= Verbosity::High) {
out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl;
}
if (!testCaseInfo.tags.empty() &&
verbosity > Verbosity::Quiet) {
out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n';
}
}
if (isFiltered) {
out << pluralise(tests.size(), "matching test case") << '\n' << std::endl;
} else {
out << pluralise(tests.size(), "test case") << '\n' << std::endl;
}
}
} // namespace Catch } // namespace Catch

View File

@ -12,9 +12,13 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <catch2/internal/catch_list.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
namespace Catch { namespace Catch {
struct IConfig; struct IConfig;
class TestCaseHandle;
// Returns double formatted as %.3f (format expected on output) // Returns double formatted as %.3f (format expected on output)
std::string getFormattedDuration( double duration ); std::string getFormattedDuration( double duration );
@ -31,6 +35,42 @@ namespace Catch {
friend std::ostream& operator<<( std::ostream& out, lineOfChars value ); friend std::ostream& operator<<( std::ostream& out, lineOfChars value );
}; };
/**
* Lists reporter descriptions to the provided stream in user-friendly
* format
*
* Used as the default listing implementation by the first party reporter
* bases. The output should be backwards compatible with the output of
* Catch2 v2 binaries.
*/
void
defaultListReporters( std::ostream& out,
std::vector<ReporterDescription> const& descriptions,
Verbosity verbosity );
/**
* Lists tag information to the provided stream in user-friendly format
*
* Used as the default listing implementation by the first party reporter
* bases. The output should be backwards compatible with the output of
* Catch2 v2 binaries.
*/
void defaultListTags( std::ostream& out, std::vector<TagInfo> const& tags, bool isFiltered );
/**
* Lists test case information to the provided stream in user-friendly
* format
*
* Used as the default listing implementation by the first party reporter
* bases. The output is backwards compatible with the output of Catch2
* v2 binaries, and also supports the format specific to the old
* `--list-test-names-only` option, for people who used it in integrations.
*/
void defaultListTests( std::ostream& out,
std::vector<TestCaseHandle> const& tests,
bool isFiltered,
Verbosity verbosity );
} // end namespace Catch } // end namespace Catch
#endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED #endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED