#include <catch2/catch_config.hpp>
#include <catch2/catch_user_config.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
#include <catch2/internal/catch_getenv.hpp>
#include <fstream>
namespace
Catch {
namespace
{
static
bool
enableBazelEnvSupport() {
#if defined( CATCH_CONFIG_BAZEL_SUPPORT )
return
true
;
#else
return
Detail::getEnv(
"BAZEL_TEST"
) !=
nullptr
;
#endif
}
struct
bazelShardingOptions {
unsigned
int
shardIndex, shardCount;
std::string shardFilePath;
};
static
Optional<bazelShardingOptions> readBazelShardingOptions() {
const
auto
bazelShardIndex = Detail::getEnv(
"TEST_SHARD_INDEX"
);
const
auto
bazelShardTotal = Detail::getEnv(
"TEST_TOTAL_SHARDS"
);
const
auto
bazelShardInfoFile = Detail::getEnv(
"TEST_SHARD_STATUS_FILE"
);
const
bool
has_all =
bazelShardIndex && bazelShardTotal && bazelShardInfoFile;
if
( !has_all ) {
auto
warn = [](
const
char
* env_var ) {
Catch::cerr()
<<
"Warning: Bazel shard configuration is missing '"
<< env_var <<
"'. Shard configuration is skipped.\n"
;
};
if
( !bazelShardIndex ) {
warn(
"TEST_SHARD_INDEX"
);
}
if
( !bazelShardTotal ) {
warn(
"TEST_TOTAL_SHARDS"
);
}
if
( !bazelShardInfoFile ) {
warn(
"TEST_SHARD_STATUS_FILE"
);
}
return
{};
}
auto
shardIndex = parseUInt( bazelShardIndex );
if
( !shardIndex ) {
Catch::cerr()
<<
"Warning: could not parse 'TEST_SHARD_INDEX' ('"
<< bazelShardIndex
<<
"') as unsigned int.\n"
;
return
{};
}
auto
shardTotal = parseUInt( bazelShardTotal );
if
( !shardTotal ) {
Catch::cerr()
<<
"Warning: could not parse 'TEST_TOTAL_SHARD' ('"
<< bazelShardTotal <<
"') as unsigned int.\n"
;
return
{};
}
return
bazelShardingOptions{
*shardIndex, *shardTotal, bazelShardInfoFile };
}
}
bool
operator==( ProcessedReporterSpec
const
& lhs,
ProcessedReporterSpec
const
& rhs ) {
return
lhs.name == rhs.name &&
lhs.outputFilename == rhs.outputFilename &&
lhs.colourMode == rhs.colourMode &&
lhs.customOptions == rhs.customOptions;
}
Config::Config( ConfigData
const
& data ):
m_data( data ) {
for
(
auto
& elem : m_data.testsOrTags) {
elem = trim(elem);
}
for
(
auto
& elem : m_data.sectionsToRun) {
elem = trim(elem);
}
if
( m_data.reporterSpecifications.empty() ) {
#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
const
auto
default_spec = CATCH_CONFIG_DEFAULT_REPORTER;
#else
const
auto
default_spec =
"console"
;
#endif
auto
parsed = parseReporterSpec(default_spec);
CATCH_ENFORCE( parsed,
"Cannot parse the provided default reporter spec: '"
<< default_spec <<
'\''
);
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
}
if
( enableBazelEnvSupport() ) {
readBazelEnvVars();
}
TestSpecParser parser( ITagAliasRegistry::get() );
if
( !m_data.testsOrTags.empty() ) {
m_hasTestFilters =
true
;
for
(
auto
const
& testOrTags : m_data.testsOrTags ) {
parser.parse( testOrTags );
}
}
m_testSpec = parser.testSpec();
bool
defaultOutputUsed =
false
;
for
(
auto
const
& reporterSpec : m_data.reporterSpecifications ) {
if
( reporterSpec.outputFile().none() ) {
CATCH_ENFORCE( !defaultOutputUsed,
"Internal error: cannot use default output for "
"multiple reporters"
);
defaultOutputUsed =
true
;
}
m_processedReporterSpecs.push_back( ProcessedReporterSpec{
reporterSpec.name(),
reporterSpec.outputFile() ? *reporterSpec.outputFile()
: data.defaultOutputFilename,
reporterSpec.colourMode().valueOr( data.defaultColourMode ),
reporterSpec.customOptions() } );
}
}
Config::~Config() =
default
;
bool
Config::listTests()
const
{
return
m_data.listTests; }
bool
Config::listTags()
const
{
return
m_data.listTags; }
bool
Config::listReporters()
const
{
return
m_data.listReporters; }
bool
Config::listListeners()
const
{
return
m_data.listListeners; }
std::vector<std::string>
const
& Config::getTestsOrTags()
const
{
return
m_data.testsOrTags; }
std::vector<std::string>
const
& Config::getSectionsToRun()
const
{
return
m_data.sectionsToRun; }
std::vector<ReporterSpec>
const
& Config::getReporterSpecs()
const
{
return
m_data.reporterSpecifications;
}
std::vector<ProcessedReporterSpec>
const
&
Config::getProcessedReporterSpecs()
const
{
return
m_processedReporterSpecs;
}
TestSpec
const
& Config::testSpec()
const
{
return
m_testSpec; }
bool
Config::hasTestFilters()
const
{
return
m_hasTestFilters; }
bool
Config::showHelp()
const
{
return
m_data.showHelp; }
bool
Config::allowThrows()
const
{
return
!m_data.noThrow; }
StringRef Config::name()
const
{
return
m_data.name.empty() ? m_data.processName : m_data.name; }
bool
Config::includeSuccessfulResults()
const
{
return
m_data.showSuccessfulTests; }
bool
Config::warnAboutMissingAssertions()
const
{
return
!!( m_data.warnings & WarnAbout::NoAssertions );
}
bool
Config::warnAboutUnmatchedTestSpecs()
const
{
return
!!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
}
bool
Config::zeroTestsCountAsSuccess()
const
{
return
m_data.allowZeroTests; }
ShowDurations Config::showDurations()
const
{
return
m_data.showDurations; }
double
Config::minDuration()
const
{
return
m_data.minDuration; }
TestRunOrder Config::runOrder()
const
{
return
m_data.runOrder; }
uint32_t Config::rngSeed()
const
{
return
m_data.rngSeed; }
unsigned
int
Config::shardCount()
const
{
return
m_data.shardCount; }
unsigned
int
Config::shardIndex()
const
{
return
m_data.shardIndex; }
ColourMode Config::defaultColourMode()
const
{
return
m_data.defaultColourMode; }
bool
Config::shouldDebugBreak()
const
{
return
m_data.shouldDebugBreak; }
int
Config::abortAfter()
const
{
return
m_data.abortAfter; }
bool
Config::showInvisibles()
const
{
return
m_data.showInvisibles; }
Verbosity Config::verbosity()
const
{
return
m_data.verbosity; }
bool
Config::skipBenchmarks()
const
{
return
m_data.skipBenchmarks; }
bool
Config::benchmarkNoAnalysis()
const
{
return
m_data.benchmarkNoAnalysis; }
unsigned
int
Config::benchmarkSamples()
const
{
return
m_data.benchmarkSamples; }
double
Config::benchmarkConfidenceInterval()
const
{
return
m_data.benchmarkConfidenceInterval; }
unsigned
int
Config::benchmarkResamples()
const
{
return
m_data.benchmarkResamples; }
std::chrono::milliseconds Config::benchmarkWarmupTime()
const
{
return
std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
void
Config::readBazelEnvVars() {
const
auto
bazelOutputFile = Detail::getEnv(
"XML_OUTPUT_FILE"
);
if
( bazelOutputFile ) {
m_data.reporterSpecifications.push_back(
{
"junit"
, std::string( bazelOutputFile ), {}, {} } );
}
const
auto
bazelTestSpec = Detail::getEnv(
"TESTBRIDGE_TEST_ONLY"
);
if
( bazelTestSpec ) {
m_data.testsOrTags.clear();
m_data.testsOrTags.push_back( bazelTestSpec );
}
const
auto
bazelShardOptions = readBazelShardingOptions();
if
( bazelShardOptions ) {
std::ofstream f( bazelShardOptions->shardFilePath,
std::ios_base::out | std::ios_base::trunc );
if
( f.is_open() ) {
f <<
""
;
m_data.shardIndex = bazelShardOptions->shardIndex;
m_data.shardCount = bazelShardOptions->shardCount;
}
}
}
}