mirror of
https://github.com/catchorg/Catch2.git
synced 2025-11-15 23:09:36 +01:00
Move files into catch2/ subdir
This commit is contained in:
302
src/catch2/catch_session.cpp
Normal file
302
src/catch2/catch_session.cpp
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Created by Martin on 31/08/2017.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include "catch_session.h"
|
||||
#include "catch_commandline.h"
|
||||
#include "catch_console_colour.h"
|
||||
#include "catch_enforce.h"
|
||||
#include "catch_list.h"
|
||||
#include "catch_context.h"
|
||||
#include "catch_run_context.h"
|
||||
#include "catch_stream.h"
|
||||
#include "catch_test_spec.h"
|
||||
#include "catch_version.h"
|
||||
#include "catch_interfaces_reporter.h"
|
||||
#include "catch_random_number_generator.h"
|
||||
#include "catch_startup_exception_registry.h"
|
||||
#include "catch_text.h"
|
||||
#include "catch_stream.h"
|
||||
#include "catch_windows_h_proxy.h"
|
||||
#include "reporters/catch_reporter_listening.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
namespace {
|
||||
const int MaxExitCode = 255;
|
||||
|
||||
IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
|
||||
auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
|
||||
CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
|
||||
|
||||
return reporter;
|
||||
}
|
||||
|
||||
IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
|
||||
if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
|
||||
return createReporter(config->getReporterName(), config);
|
||||
}
|
||||
|
||||
// On older platforms, returning std::unique_ptr<ListeningReporter>
|
||||
// when the return type is std::unique_ptr<IStreamingReporter>
|
||||
// doesn't compile without a std::move call. However, this causes
|
||||
// a warning on newer platforms. Thus, we have to work around
|
||||
// it a bit and downcast the pointer manually.
|
||||
auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
|
||||
auto& multi = static_cast<ListeningReporter&>(*ret);
|
||||
auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
|
||||
for (auto const& listener : listeners) {
|
||||
multi.addListener(listener->create(Catch::ReporterConfig(config)));
|
||||
}
|
||||
multi.addReporter(createReporter(config->getReporterName(), config));
|
||||
return ret;
|
||||
}
|
||||
|
||||
class TestGroup {
|
||||
public:
|
||||
explicit TestGroup(IStreamingReporterPtr&& reporter, std::shared_ptr<Config> const& config)
|
||||
: m_config{config}
|
||||
, m_context{config, std::move(reporter)}
|
||||
{
|
||||
auto const& allTestCases = getAllTestCasesSorted(*m_config);
|
||||
m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
|
||||
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
|
||||
|
||||
if (m_matches.empty() && invalidArgs.empty()) {
|
||||
for (auto const& test : allTestCases)
|
||||
if (!test.getTestCaseInfo().isHidden())
|
||||
m_tests.emplace(&test);
|
||||
} else {
|
||||
for (auto const& match : m_matches)
|
||||
m_tests.insert(match.tests.begin(), match.tests.end());
|
||||
}
|
||||
}
|
||||
|
||||
Totals execute() {
|
||||
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
|
||||
Totals totals;
|
||||
m_context.testGroupStarting(m_config->name(), 1, 1);
|
||||
for (auto const& testCase : m_tests) {
|
||||
if (!m_context.aborting())
|
||||
totals += m_context.runTest(*testCase);
|
||||
else
|
||||
m_context.reporter().skipTest(testCase->getTestCaseInfo());
|
||||
}
|
||||
|
||||
for (auto const& match : m_matches) {
|
||||
if (match.tests.empty()) {
|
||||
m_context.reporter().noMatchingTestCases(match.name);
|
||||
totals.error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!invalidArgs.empty()) {
|
||||
for (auto const& invalidArg: invalidArgs)
|
||||
m_context.reporter().reportInvalidArguments(invalidArg);
|
||||
}
|
||||
|
||||
m_context.testGroupEnded(m_config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
|
||||
private:
|
||||
using Tests = std::set<TestCaseHandle const*>;
|
||||
|
||||
std::shared_ptr<Config> m_config;
|
||||
RunContext m_context;
|
||||
Tests m_tests;
|
||||
TestSpec::Matches m_matches;
|
||||
};
|
||||
|
||||
void applyFilenamesAsTags() {
|
||||
for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) {
|
||||
testInfo->addFilenameTag();
|
||||
}
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
Session::Session() {
|
||||
static bool alreadyInstantiated = false;
|
||||
if( alreadyInstantiated ) {
|
||||
CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
|
||||
CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
|
||||
}
|
||||
|
||||
// There cannot be exceptions at startup in no-exception mode.
|
||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
|
||||
if ( !exceptions.empty() ) {
|
||||
config();
|
||||
getCurrentMutableContext().setConfig(m_config);
|
||||
|
||||
m_startupExceptions = true;
|
||||
Colour colourGuard( Colour::Red );
|
||||
Catch::cerr() << "Errors occurred 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() << Column( ex.what() ).indent(2) << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
alreadyInstantiated = true;
|
||||
m_cli = makeCommandLineParser( m_configData );
|
||||
}
|
||||
Session::~Session() {
|
||||
Catch::cleanUp();
|
||||
}
|
||||
|
||||
void Session::showHelp() const {
|
||||
Catch::cout()
|
||||
<< "\nCatch v" << libraryVersion() << "\n"
|
||||
<< m_cli << std::endl
|
||||
<< "For more detailed usage please see the project docs\n" << std::endl;
|
||||
}
|
||||
void Session::libIdentify() {
|
||||
Catch::cout()
|
||||
<< std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
|
||||
<< std::left << std::setw(16) << "category: " << "testframework\n"
|
||||
<< std::left << std::setw(16) << "framework: " << "Catch Test\n"
|
||||
<< std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
|
||||
}
|
||||
|
||||
int Session::applyCommandLine( int argc, char const * const * argv ) {
|
||||
if( m_startupExceptions )
|
||||
return 1;
|
||||
|
||||
auto result = m_cli.parse( clara::Args( argc, argv ) );
|
||||
if( !result ) {
|
||||
config();
|
||||
getCurrentMutableContext().setConfig(m_config);
|
||||
Catch::cerr()
|
||||
<< Colour( Colour::Red )
|
||||
<< "\nError(s) in input:\n"
|
||||
<< Column( result.errorMessage() ).indent( 2 )
|
||||
<< "\n\n";
|
||||
Catch::cerr() << "Run with -? for usage\n" << std::endl;
|
||||
return MaxExitCode;
|
||||
}
|
||||
|
||||
if( m_configData.showHelp )
|
||||
showHelp();
|
||||
if( m_configData.libIdentify )
|
||||
libIdentify();
|
||||
m_config.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
||||
int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
|
||||
|
||||
char **utf8Argv = new char *[ argc ];
|
||||
|
||||
for ( int i = 0; i < argc; ++i ) {
|
||||
int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
|
||||
|
||||
utf8Argv[ i ] = new char[ bufSize ];
|
||||
|
||||
WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
|
||||
}
|
||||
|
||||
int returnCode = applyCommandLine( argc, utf8Argv );
|
||||
|
||||
for ( int i = 0; i < argc; ++i )
|
||||
delete [] utf8Argv[ i ];
|
||||
|
||||
delete [] utf8Argv;
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Session::useConfigData( ConfigData const& configData ) {
|
||||
m_configData = configData;
|
||||
m_config.reset();
|
||||
}
|
||||
|
||||
int Session::run() {
|
||||
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
|
||||
Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
|
||||
static_cast<void>(std::getchar());
|
||||
}
|
||||
int exitCode = runInternal();
|
||||
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
|
||||
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
|
||||
static_cast<void>(std::getchar());
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
clara::Parser const& Session::cli() const {
|
||||
return m_cli;
|
||||
}
|
||||
void Session::cli( clara::Parser const& newParser ) {
|
||||
m_cli = newParser;
|
||||
}
|
||||
ConfigData& Session::configData() {
|
||||
return m_configData;
|
||||
}
|
||||
Config& Session::config() {
|
||||
if( !m_config )
|
||||
m_config = std::make_shared<Config>( m_configData );
|
||||
return *m_config;
|
||||
}
|
||||
|
||||
int Session::runInternal() {
|
||||
if( m_startupExceptions )
|
||||
return 1;
|
||||
|
||||
if (m_configData.showHelp || m_configData.libIdentify) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
CATCH_TRY {
|
||||
config(); // Force config to be constructed
|
||||
|
||||
seedRng( *m_config );
|
||||
|
||||
if (m_configData.filenamesAsTags) {
|
||||
applyFilenamesAsTags();
|
||||
}
|
||||
|
||||
// Create reporter(s) so we can route listings through them
|
||||
auto reporter = makeReporter(m_config);
|
||||
|
||||
// Handle list request
|
||||
if (list(*reporter, m_config)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
TestGroup tests { std::move(reporter), m_config };
|
||||
auto const totals = tests.execute();
|
||||
|
||||
if( m_config->warnAboutNoTests() && totals.error == -1 )
|
||||
return 2;
|
||||
|
||||
// Note that on unices only the lower 8 bits are usually used, clamping
|
||||
// the return value to 255 prevents false negative when some multiple
|
||||
// of 256 tests has failed
|
||||
return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
|
||||
}
|
||||
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
catch( std::exception& ex ) {
|
||||
Catch::cerr() << ex.what() << std::endl;
|
||||
return MaxExitCode;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
||||
Reference in New Issue
Block a user