mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-23 05:46:11 +01:00
da0edcbe25
Previously, some errors in Catch configuration would cause exceptions to be thrown before main was even entered. This leads to call to `std::terminate`, which is not a particularly nice way of ending the binary. Now these exceptions are registered with a global collector and used once Catch enters main. They can also be optionally ignored, if user supplies his own main and opts not to check them (or ignored them intentionally). Closes #921
220 lines
7.8 KiB
C++
220 lines
7.8 KiB
C++
/*
|
|
* Created by Phil on 31/10/2010.
|
|
* Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
|
|
*
|
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
#ifndef TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
|
|
#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
|
|
|
|
#include "internal/catch_commandline.hpp"
|
|
#include "internal/catch_list.hpp"
|
|
#include "internal/catch_run_context.hpp"
|
|
#include "internal/catch_test_spec.hpp"
|
|
#include "internal/catch_version.h"
|
|
#include "internal/catch_text.h"
|
|
#include "internal/catch_interfaces_reporter.h"
|
|
#include "internal/catch_startup_exception_registry.h"
|
|
|
|
#include <fstream>
|
|
#include <stdlib.h>
|
|
#include <limits>
|
|
|
|
namespace Catch {
|
|
|
|
IStreamingReporterPtr createReporter( std::string const& reporterName, IConfigPtr const& config ) {
|
|
auto reporter = getRegistryHub().getReporterRegistry().create( reporterName, config );
|
|
CATCH_ENFORCE( reporter, "No reporter registered with name: '" << reporterName << "'" );
|
|
|
|
return reporter;
|
|
}
|
|
|
|
IStreamingReporterPtr makeReporter( std::shared_ptr<Config> const& config ) {
|
|
auto const& reporterNames = config->getReporterNames();
|
|
if( reporterNames.empty() )
|
|
return createReporter( "console", config );
|
|
|
|
IStreamingReporterPtr reporter;
|
|
for( auto const& name : reporterNames )
|
|
addReporter( reporter, createReporter( name, config ) );
|
|
return reporter;
|
|
}
|
|
void addListeners( IStreamingReporterPtr& reporters, IConfigPtr const& config ) {
|
|
auto const& listeners = getRegistryHub().getReporterRegistry().getListeners();
|
|
for( auto const& listener : listeners )
|
|
addReporter(reporters, listener->create( ReporterConfig( config ) ) );
|
|
}
|
|
|
|
|
|
Totals runTests( std::shared_ptr<Config> const& config ) {
|
|
|
|
IConfigPtr iconfig = config;
|
|
|
|
IStreamingReporterPtr reporter = makeReporter( config );
|
|
addListeners( reporter, iconfig );
|
|
|
|
RunContext context( iconfig, std::move( reporter ) );
|
|
|
|
Totals totals;
|
|
|
|
context.testGroupStarting( config->name(), 1, 1 );
|
|
|
|
TestSpec testSpec = config->testSpec();
|
|
if( !testSpec.hasFilters() )
|
|
testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
|
|
|
|
std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
|
|
for( auto const& testCase : allTestCases ) {
|
|
if( !context.aborting() && matchTest( testCase, testSpec, *iconfig ) )
|
|
totals += context.runTest( testCase );
|
|
else
|
|
context.reporter().skipTest( testCase );
|
|
}
|
|
|
|
context.testGroupEnded( iconfig->name(), totals, 1, 1 );
|
|
return totals;
|
|
}
|
|
|
|
void applyFilenamesAsTags( IConfig const& config ) {
|
|
auto& tests = const_cast<std::vector<TestCase>&>( getAllTestCasesSorted( config ) );
|
|
for( auto& testCase : tests ) {
|
|
std::set<std::string> tags = testCase.tags;
|
|
|
|
std::string filename = testCase.lineInfo.file;
|
|
std::string::size_type lastSlash = filename.find_last_of( "\\/" );
|
|
if( lastSlash != std::string::npos )
|
|
filename = filename.substr( lastSlash+1 );
|
|
|
|
std::string::size_type lastDot = filename.find_last_of( "." );
|
|
if( lastDot != std::string::npos )
|
|
filename = filename.substr( 0, lastDot );
|
|
|
|
tags.insert( "#" + filename );
|
|
setTags( testCase, tags );
|
|
}
|
|
}
|
|
|
|
class Session : NonCopyable {
|
|
static bool alreadyInstantiated;
|
|
|
|
public:
|
|
|
|
struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
|
|
|
|
Session()
|
|
: m_cli( makeCommandLineParser() ) {
|
|
if( alreadyInstantiated )
|
|
CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" );
|
|
alreadyInstantiated = true;
|
|
}
|
|
~Session() {
|
|
Catch::cleanUp();
|
|
}
|
|
|
|
void showHelp( std::string const& processName ) {
|
|
Catch::cout() << "\nCatch v" << libraryVersion() << "\n";
|
|
|
|
m_cli.usage( Catch::cout(), processName );
|
|
Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
|
|
}
|
|
|
|
int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
|
|
try {
|
|
m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
|
|
m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
|
|
if( m_configData.showHelp )
|
|
showHelp( m_configData.processName );
|
|
m_config.reset();
|
|
}
|
|
catch( std::exception& ex ) {
|
|
{
|
|
Colour colourGuard( Colour::Red );
|
|
Catch::cerr()
|
|
<< "\nError(s) in input:\n"
|
|
<< Text( ex.what(), TextAttributes().setIndent(2) )
|
|
<< "\n\n";
|
|
}
|
|
m_cli.usage( Catch::cout(), m_configData.processName );
|
|
return (std::numeric_limits<int>::max)();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void useConfigData( ConfigData const& _configData ) {
|
|
m_configData = _configData;
|
|
m_config.reset();
|
|
}
|
|
|
|
int run( int argc, char const* const* const argv ) {
|
|
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
|
|
if ( !exceptions.empty() ) {
|
|
Catch::cerr() << "Errors occured during startup!" << '\n';
|
|
// iterate over all exceptions and notify user
|
|
for ( const auto& ex_ptr : exceptions ) {
|
|
try {
|
|
std::rethrow_exception(ex_ptr);
|
|
} catch ( std::exception const& ex ) {
|
|
Catch::cerr() << ex.what() << '\n';
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
int returnCode = applyCommandLine( argc, argv );
|
|
if( returnCode == 0 )
|
|
returnCode = run();
|
|
return returnCode;
|
|
}
|
|
|
|
int run() {
|
|
if( m_configData.showHelp )
|
|
return 0;
|
|
|
|
try
|
|
{
|
|
config(); // Force config to be constructed
|
|
|
|
seedRng( *m_config );
|
|
|
|
if( m_configData.filenamesAsTags )
|
|
applyFilenamesAsTags( *m_config );
|
|
|
|
// Handle list request
|
|
if( Option<std::size_t> listed = list( config() ) )
|
|
return static_cast<int>( *listed );
|
|
|
|
return static_cast<int>( runTests( m_config ).assertions.failed );
|
|
}
|
|
catch( std::exception& ex ) {
|
|
Catch::cerr() << ex.what() << std::endl;
|
|
return (std::numeric_limits<int>::max)();
|
|
}
|
|
}
|
|
|
|
Clara::CommandLine<ConfigData> const& cli() const {
|
|
return m_cli;
|
|
}
|
|
std::vector<Clara::Parser::Token> const& unusedTokens() const {
|
|
return m_unusedTokens;
|
|
}
|
|
ConfigData& configData() {
|
|
return m_configData;
|
|
}
|
|
Config& config() {
|
|
if( !m_config )
|
|
m_config = std::make_shared<Config>( m_configData );
|
|
return *m_config;
|
|
}
|
|
private:
|
|
Clara::CommandLine<ConfigData> m_cli;
|
|
std::vector<Clara::Parser::Token> m_unusedTokens;
|
|
ConfigData m_configData;
|
|
std::shared_ptr<Config> m_config;
|
|
};
|
|
|
|
bool Session::alreadyInstantiated = false;
|
|
|
|
} // end namespace Catch
|
|
|
|
#endif // TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
|