diff --git a/docs/command-line.md b/docs/command-line.md
index 29e6a365..829c156d 100644
--- a/docs/command-line.md
+++ b/docs/command-line.md
@@ -123,22 +123,33 @@ Test names containing special characters, such as `,` or `[` can specify them on
## Choosing a reporter to use
-
-r, --reporter <reporter[::output-file]>
-
-> Support for providing output-file through the `-r`, `--reporter` flag was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 X.Y.Z
+-r, --reporter <reporter[::key=value]*>
Reporters are how the output from Catch2 (results of assertions, tests,
benchmarks and so on) is formatted and written out. The default reporter
is called the "Console" reporter and is intended to provide relatively
verbose and human-friendly output.
+Reporters are also individually configurable. To pass configuration options
+to the reporter, you append `::key=value` to the reporter specification
+as many times as you want, e.g. `--reporter xml::out=someFile.xml`.
+
+The keys must either be prefixed by "X", in which case they are not parsed
+by Catch2 and are only passed down to the reporter, or one of options
+hardcoded into Catch2. Currently there are only 2,
+["out"](#sending-output-to-a-file), and ["colour-mode"](#colour-mode).
+
+_Note that the reporter might still check the X-prefixed options for
+validity, and throw an error if they are wrong._
+
+> Support for passing arguments to reporters through the `-r`, `--reporter` flag was introduced in Catch2 X.Y.Z
+
There are multiple built-in reporters, you can see what they do by using the
[`--list-reporter`](command-line.md#listing-available-tests-tags-or-reporters)
flag. If you need a reporter providing custom format outside of the already
provided ones, look at the ["write your own reporter" part of the reporter
documentation](reporters.md#writing-your-own-reporter).
-
This option may be passed multiple times to use multiple (different)
reporters at the same time. See the [reporter documentation](reporters.md#multiple-reporters)
for details on what the resulting behaviour is. Also note that at most one
@@ -148,13 +159,12 @@ the [`-o`, `--out`](#sending-output-to-a-file) option.
> Support for using multiple different reporters at the same time was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 X.Y.Z
-As with the `--out` option, using `-` for the output file name sends the
-output to stdout.
_Note: There is currently no way to escape `::` in the reporter spec,
-and thus reporter/file names with `::` in them will not work properly.
-As `::` in paths is relatively obscure (unlike `:`), we do not consider
-this an issue._
+and thus the reporter names, or configuration keys and values, cannot
+contain `::`. As `::` in paths is relatively obscure (unlike ':'), we do
+not consider this an issue._
+
## Breaking into the debugger
diff --git a/docs/reporters.md b/docs/reporters.md
index ab6f6453..5f86b02e 100644
--- a/docs/reporters.md
+++ b/docs/reporters.md
@@ -43,9 +43,14 @@ them write into different destinations. The two main uses of this are
Specifying multiple reporter looks like this:
```
---reporter console::- --reporter JUnit::result-junit.xml
+--reporter JUnit::out=result-junit.xml --reporter console::out=-::colour-mode=ansi
```
+This tells Catch2 to use two reporters, `JUnit` reporter that writes
+its machine-readable XML output to file `result-junit.xml`, and the
+`console` reporter that writes its user-friendly output to stdout and
+uses ANSI colour codes for colouring the output.
+
Using multiple reporters (or one reporter and one-or-more [event
listeners](event-listener.md#top)) can have surprisingly complex semantics
when using customization points provided to reporters by Catch2, namely
@@ -162,6 +167,26 @@ Currently there are two customization options:
format includes passing assertions even without the `-s` flag.
+### Per-reporter configuration
+
+> Per-reporter configuration was introduced in Catch2 X.Y.Z
+
+Catch2 supports some configuration to happen per reporter. The configuration
+options fall into one of two categories:
+
+* Catch2-recognized options
+* Reporter-specific options
+
+The former is a small set of universal options that Catch2 handles for
+the reporters, e.g. output file or console colour mode. The latter are
+options that the reporters have to handle themselves, but the keys and
+values can be arbitrary strings, as long as they don't contain `::`. This
+allows writing reporters that can be significantly customized at runtime.
+
+Reporter-specific options always have to be prefixed with "X" (large
+letter X).
+
+
### Other expected functionality of a reporter
When writing a custom reporter, there are few more things that you should
diff --git a/src/catch2/catch_config.cpp b/src/catch2/catch_config.cpp
index 4c8b3319..75a92883 100644
--- a/src/catch2/catch_config.cpp
+++ b/src/catch2/catch_config.cpp
@@ -33,17 +33,6 @@ namespace Catch {
} // unnamed namespace
} // namespace Detail
- std::ostream& operator<<( std::ostream& os,
- ConfigData::ReporterAndFile const& reporter ) {
- os << "{ " << reporter.reporterName << ", ";
- if ( reporter.outputFileName ) {
- os << *reporter.outputFileName;
- } else {
- os << "";
- }
- return os << " }";
- }
-
Config::Config( ConfigData const& data ):
m_data( data ) {
// We need to trim filter specs to avoid trouble with superfluous
@@ -76,14 +65,14 @@ namespace Catch {
#else
"console",
#endif
- {}
+ {}, {}, {}
} );
}
bool defaultOutputUsed = false;
m_reporterStreams.reserve( m_data.reporterSpecifications.size() );
- for ( auto const& reporterAndFile : m_data.reporterSpecifications ) {
- if ( reporterAndFile.outputFileName.none() ) {
+ for ( auto const& reporterSpec : m_data.reporterSpecifications ) {
+ if ( reporterSpec.outputFile().none() ) {
CATCH_ENFORCE( !defaultOutputUsed,
"Internal error: cannot use default output for "
"multiple reporters" );
@@ -93,7 +82,7 @@ namespace Catch {
openStream( data.defaultOutputFilename ) );
} else {
m_reporterStreams.push_back(
- openStream( *reporterAndFile.outputFileName ) );
+ openStream( *reporterSpec.outputFile() ) );
}
}
}
@@ -108,7 +97,7 @@ namespace Catch {
std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
- std::vector const& Config::getReportersAndOutputFiles() const {
+ std::vector const& Config::getReporterSpecs() const {
return m_data.reporterSpecifications;
}
diff --git a/src/catch2/catch_config.hpp b/src/catch2/catch_config.hpp
index 91ce4e64..324d634c 100644
--- a/src/catch2/catch_config.hpp
+++ b/src/catch2/catch_config.hpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include
#include
@@ -24,17 +25,6 @@ namespace Catch {
class IStream;
struct ConfigData {
- struct ReporterAndFile {
- std::string reporterName;
-
- // If none, the output goes to the default output.
- Optional outputFileName;
-
- friend bool operator==(ReporterAndFile const& lhs, ReporterAndFile const& rhs) {
- return lhs.reporterName == rhs.reporterName && lhs.outputFileName == rhs.outputFileName;
- }
- friend std::ostream& operator<<(std::ostream &os, ReporterAndFile const& reporter);
- };
bool listTests = false;
bool listTags = false;
@@ -72,7 +62,7 @@ namespace Catch {
std::string defaultOutputFilename;
std::string name;
std::string processName;
- std::vector reporterSpecifications;
+ std::vector reporterSpecifications;
std::vector testsOrTags;
std::vector sectionsToRun;
@@ -90,7 +80,7 @@ namespace Catch {
bool listTags() const;
bool listReporters() const;
- std::vector const& getReportersAndOutputFiles() const;
+ std::vector const& getReporterSpecs() const;
IStream const* getReporterOutputStream(std::size_t reporterIdx) const;
std::vector const& getTestsOrTags() const override;
diff --git a/src/catch2/catch_session.cpp b/src/catch2/catch_session.cpp
index 6af52685..74731cd0 100644
--- a/src/catch2/catch_session.cpp
+++ b/src/catch2/catch_session.cpp
@@ -41,11 +41,18 @@ namespace Catch {
return reporter;
}
- IStreamingReporterPtr makeReporter(Config const* config) {
+ IStreamingReporterPtr prepareReporters(Config const* config) {
if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()
- && config->getReportersAndOutputFiles().size() == 1) {
+ && config->getReporterSpecs().size() == 1) {
+ auto const& spec = config->getReporterSpecs()[0];
auto stream = config->getReporterOutputStream(0);
- return createReporter(config->getReportersAndOutputFiles()[0].reporterName, ReporterConfig(config, stream));
+ return createReporter(
+ config->getReporterSpecs()[0].name(),
+ ReporterConfig(
+ config,
+ stream,
+ spec.colourMode().valueOr( config->colourMode() ),
+ spec.customOptions() ) );
}
auto multi = Detail::make_unique(config);
@@ -56,9 +63,15 @@ namespace Catch {
}
std::size_t reporterIdx = 0;
- for (auto const& reporterAndFile : config->getReportersAndOutputFiles()) {
+ for (auto const& reporterSpec : config->getReporterSpecs()) {
auto stream = config->getReporterOutputStream(reporterIdx);
- multi->addReporter(createReporter(reporterAndFile.reporterName, ReporterConfig(config, stream)));
+ multi->addReporter( createReporter(
+ reporterSpec.name(),
+ ReporterConfig( config,
+ stream,
+ reporterSpec.colourMode().valueOr(
+ config->colourMode() ),
+ reporterSpec.customOptions() ) ) );
reporterIdx++;
}
@@ -304,7 +317,7 @@ namespace Catch {
getCurrentMutableContext().setConfig(m_config.get());
// Create reporter(s) so we can route listings through them
- auto reporter = makeReporter(m_config.get());
+ auto reporter = prepareReporters(m_config.get());
auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs();
if ( !invalidSpecs.empty() ) {
diff --git a/src/catch2/interfaces/catch_interfaces_reporter.cpp b/src/catch2/interfaces/catch_interfaces_reporter.cpp
index 5f5d274a..585d4365 100644
--- a/src/catch2/interfaces/catch_interfaces_reporter.cpp
+++ b/src/catch2/interfaces/catch_interfaces_reporter.cpp
@@ -14,17 +14,31 @@
#include
#include
#include
+#include
#include
#include
namespace Catch {
- ReporterConfig::ReporterConfig( IConfig const* _fullConfig, IStream const* _stream )
- : m_stream( _stream ), m_fullConfig( _fullConfig ) {}
+ ReporterConfig::ReporterConfig(
+ IConfig const* _fullConfig,
+ IStream const* _stream,
+ ColourMode colourMode,
+ std::map customOptions ):
+ m_stream( _stream ),
+ m_fullConfig( _fullConfig ),
+ m_colourMode( colourMode ),
+ m_customOptions( CATCH_MOVE( customOptions ) ) {}
IStream const* ReporterConfig::stream() const { return m_stream; }
IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
+ ColourMode ReporterConfig::colourMode() const { return m_colourMode; }
+
+ std::map const&
+ ReporterConfig::customOptions() const {
+ return m_customOptions;
+ }
AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
std::vector const& _infoMessages,
diff --git a/src/catch2/interfaces/catch_interfaces_reporter.hpp b/src/catch2/interfaces/catch_interfaces_reporter.hpp
index 53d5ae60..9c367fe9 100644
--- a/src/catch2/interfaces/catch_interfaces_reporter.hpp
+++ b/src/catch2/interfaces/catch_interfaces_reporter.hpp
@@ -19,6 +19,7 @@
#include
+#include