mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-22 19:33:29 +01:00
Add helper for parsing the new reporter specs
The new reporter spec generalizes key-value options that can be passed to the reporter, looking like this `reporterName[::key=value]*`. A key can be either Catch2-recognized, which currently means either `out` or `colour`, or reporter-specific which is anything prefixed with `X`, e.g. `Xfoo`.
This commit is contained in:
parent
8ac86495de
commit
a51fd07bd0
@ -9,9 +9,28 @@
|
||||
#include <catch2/internal/catch_reporter_spec_parser.hpp>
|
||||
|
||||
#include <catch2/interfaces/catch_interfaces_config.hpp>
|
||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
struct kvPair {
|
||||
StringRef key, value;
|
||||
};
|
||||
|
||||
kvPair splitKVPair(StringRef kvString) {
|
||||
auto splitPos = static_cast<size_t>( std::distance(
|
||||
kvString.begin(),
|
||||
std::find( kvString.begin(), kvString.end(), '=' ) ) );
|
||||
|
||||
return { kvString.substr( 0, splitPos ),
|
||||
kvString.substr( splitPos + 1, kvString.size() ) };
|
||||
}
|
||||
}
|
||||
|
||||
namespace Detail {
|
||||
std::vector<std::string> splitReporterSpec( StringRef reporterSpec ) {
|
||||
static constexpr auto separator = "::";
|
||||
@ -78,4 +97,78 @@ namespace Catch {
|
||||
} // namespace Detail
|
||||
|
||||
|
||||
bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ) {
|
||||
return lhs.m_name == rhs.m_name &&
|
||||
lhs.m_outputFileName == rhs.m_outputFileName &&
|
||||
lhs.m_colourMode == rhs.m_colourMode &&
|
||||
lhs.m_customOptions == rhs.m_customOptions;
|
||||
}
|
||||
|
||||
Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec ) {
|
||||
auto parts = Detail::splitReporterSpec( reporterSpec );
|
||||
|
||||
assert( parts.size() > 0 && "Split should never return empty vector" );
|
||||
|
||||
std::map<std::string, std::string> kvPairs;
|
||||
Optional<std::string> outputFileName;
|
||||
Optional<ColourMode> colourMode;
|
||||
|
||||
// First part is always reporter name, so we skip it
|
||||
for ( size_t i = 1; i < parts.size(); ++i ) {
|
||||
auto kv = splitKVPair( parts[i] );
|
||||
auto key = kv.key, value = kv.value;
|
||||
|
||||
if ( key.empty() || value.empty() ) {
|
||||
return {};
|
||||
} else if ( key[0] == 'X' ) {
|
||||
// This is a reporter-specific option, we don't check these
|
||||
// apart from basic sanity checks
|
||||
if ( key.size() == 1 ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ret = kvPairs.emplace( kv.key, kv.value );
|
||||
if ( !ret.second ) {
|
||||
// Duplicated key. We might want to handle this differently,
|
||||
// e.g. by overwriting the existing value?
|
||||
return {};
|
||||
}
|
||||
} else if ( key == "out" ) {
|
||||
// Duplicated key
|
||||
if ( outputFileName ) {
|
||||
return {};
|
||||
}
|
||||
outputFileName = static_cast<std::string>( value );
|
||||
} else if ( key == "colour" ) {
|
||||
// Duplicated key
|
||||
if ( colourMode ) {
|
||||
return {};
|
||||
}
|
||||
colourMode = Detail::stringToColourMode( value );
|
||||
// Parsing failed
|
||||
if ( !colourMode ) {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
// Unrecognized option
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return ReporterSpec{ CATCH_MOVE( parts[0] ),
|
||||
CATCH_MOVE( outputFileName ),
|
||||
CATCH_MOVE( colourMode ),
|
||||
CATCH_MOVE( kvPairs ) };
|
||||
}
|
||||
|
||||
ReporterSpec::ReporterSpec(
|
||||
std::string name,
|
||||
Optional<std::string> outputFileName,
|
||||
Optional<ColourMode> colourMode,
|
||||
std::map<std::string, std::string> customOptions ):
|
||||
m_name( CATCH_MOVE( name ) ),
|
||||
m_outputFileName( CATCH_MOVE( outputFileName ) ),
|
||||
m_colourMode( CATCH_MOVE( colourMode ) ),
|
||||
m_customOptions( CATCH_MOVE( customOptions ) ) {}
|
||||
|
||||
} // namespace Catch
|
||||
|
@ -8,11 +8,13 @@
|
||||
#ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
|
||||
#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
|
||||
|
||||
#include <catch2/internal/catch_console_colour.hpp>
|
||||
#include <catch2/internal/catch_optional.hpp>
|
||||
#include <catch2/internal/catch_stringref.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@ -25,6 +27,59 @@ namespace Catch {
|
||||
Optional<ColourMode> stringToColourMode( StringRef colourMode );
|
||||
}
|
||||
|
||||
/**
|
||||
* Structured reporter spec that a reporter can be created from
|
||||
*
|
||||
* Parsing has been validated, but semantics have not. This means e.g.
|
||||
* that the colour mode is known to Catch2, but it might not be
|
||||
* compiled into the binary, and the output filename might not be
|
||||
* openable.
|
||||
*/
|
||||
class ReporterSpec {
|
||||
std::string m_name;
|
||||
Optional<std::string> m_outputFileName;
|
||||
Optional<ColourMode> m_colourMode;
|
||||
std::map<std::string, std::string> m_customOptions;
|
||||
|
||||
friend bool operator==( ReporterSpec const& lhs,
|
||||
ReporterSpec const& rhs );
|
||||
friend bool operator!=( ReporterSpec const& lhs,
|
||||
ReporterSpec const& rhs ) {
|
||||
return !( lhs == rhs );
|
||||
}
|
||||
|
||||
public:
|
||||
ReporterSpec(
|
||||
std::string name,
|
||||
Optional<std::string> outputFileName,
|
||||
Optional<ColourMode> colourMode,
|
||||
std::map<std::string, std::string> customOptions );
|
||||
|
||||
std::string const& name() const { return m_name; }
|
||||
|
||||
Optional<std::string> const& outputFile() const {
|
||||
return m_outputFileName;
|
||||
}
|
||||
|
||||
Optional<ColourMode> const& colourMode() const { return m_colourMode; }
|
||||
|
||||
std::map<std::string, std::string> const& customOptions() const {
|
||||
return m_customOptions;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses provided reporter spec string into
|
||||
*
|
||||
* Returns empty optional on errors, e.g.
|
||||
* * field that is not first and not a key+value pair
|
||||
* * duplicated keys in kv pair
|
||||
* * unknown catch reporter option
|
||||
* * empty key/value in an custom kv pair
|
||||
* * ...
|
||||
*/
|
||||
Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec );
|
||||
|
||||
}
|
||||
|
||||
#endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
|
||||
|
@ -62,3 +62,50 @@ TEST_CASE( "Parsing colour mode", "[cli][colour][approvals]" ) {
|
||||
REQUIRE_FALSE( stringToColourMode( "asdbjsdb kasbd" ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Parsing reporter specs", "[cli][reporter-spec][approvals]") {
|
||||
using Catch::parseReporterSpec;
|
||||
using Catch::ReporterSpec;
|
||||
using namespace std::string_literals;
|
||||
|
||||
SECTION( "Correct specs" ) {
|
||||
REQUIRE( parseReporterSpec( "someReporter" ) ==
|
||||
ReporterSpec( "someReporter"s, {}, {}, {} ) );
|
||||
REQUIRE( parseReporterSpec( "otherReporter::Xk=v::out=c:\\blah" ) ==
|
||||
ReporterSpec(
|
||||
"otherReporter"s, "c:\\blah"s, {}, { { "Xk"s, "v"s } } ) );
|
||||
REQUIRE( parseReporterSpec( "diffReporter::Xk1=v1::Xk2==v2" ) ==
|
||||
ReporterSpec( "diffReporter",
|
||||
{},
|
||||
{},
|
||||
{ { "Xk1"s, "v1"s }, { "Xk2"s, "=v2"s } } ) );
|
||||
REQUIRE( parseReporterSpec(
|
||||
"Foo:bar:reporter::colour=ansi::Xk 1=v 1::Xk2=v:3" ) ==
|
||||
ReporterSpec( "Foo:bar:reporter",
|
||||
{},
|
||||
Catch::ColourMode::ANSI,
|
||||
{ { "Xk 1"s, "v 1"s }, { "Xk2"s, "v:3"s } } ) );
|
||||
}
|
||||
|
||||
SECTION( "Bad specs" ) {
|
||||
REQUIRE_FALSE( parseReporterSpec( "::" ) );
|
||||
// Unknown Catch2 arg (should be "out")
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::output=filename" ) );
|
||||
// Wrong colour spec
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::colour=custom" ) );
|
||||
// Duplicated colour spec
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::colour=ansi::colour=ansi" ) );
|
||||
// Duplicated out arg
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::out=f.txt::out=z.txt" ) );
|
||||
// Duplicated custom arg
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::Xa=foo::Xa=bar" ) );
|
||||
// Empty key
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::X=foo" ) );
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::=foo" ) );
|
||||
// Empty value
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::Xa=" ) );
|
||||
// non-key value later field
|
||||
REQUIRE_FALSE( parseReporterSpec( "reporter::Xab" ) );
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user