| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  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"
 | 
					
						
							| 
									
										
										
										
											2017-09-07 11:24:33 +01:00
										 |  |  | #include "catch_commandline.h"
 | 
					
						
							|  |  |  | #include "catch_console_colour.h"
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | #include "catch_enforce.h"
 | 
					
						
							|  |  |  | #include "catch_list.h"
 | 
					
						
							| 
									
										
										
										
											2017-09-07 11:24:33 +01:00
										 |  |  | #include "catch_run_context.h"
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | #include "catch_stream.h"
 | 
					
						
							| 
									
										
										
										
											2017-09-07 11:24:33 +01:00
										 |  |  | #include "catch_test_spec.h"
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | #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"
 | 
					
						
							| 
									
										
										
										
											2018-02-09 01:31:19 +02:00
										 |  |  | #include "catch_stream.h"
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <cstdlib>
 | 
					
						
							|  |  |  | #include <iomanip>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  | namespace Catch { | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |     namespace { | 
					
						
							|  |  |  |         const int MaxExitCode = 255; | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |         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 << "'"); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             return reporter; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #ifndef CATCH_CONFIG_DEFAULT_REPORTER
 | 
					
						
							|  |  |  | #define CATCH_CONFIG_DEFAULT_REPORTER "console"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |         IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) { | 
					
						
							|  |  |  |             auto const& reporterNames = config->getReporterNames(); | 
					
						
							|  |  |  |             if (reporterNames.empty()) | 
					
						
							|  |  |  |                 return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             IStreamingReporterPtr reporter; | 
					
						
							|  |  |  |             for (auto const& name : reporterNames) | 
					
						
							|  |  |  |                 addReporter(reporter, createReporter(name, config)); | 
					
						
							|  |  |  |             return reporter; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  | #undef CATCH_CONFIG_DEFAULT_REPORTER
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |         void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { | 
					
						
							|  |  |  |             auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); | 
					
						
							|  |  |  |             for (auto const& listener : listeners) | 
					
						
							|  |  |  |                 addReporter(reporters, listener->create(Catch::ReporterConfig(config))); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |         Catch::Totals runTests(std::shared_ptr<Config> const& config) { | 
					
						
							|  |  |  |             IStreamingReporterPtr reporter = makeReporter(config); | 
					
						
							|  |  |  |             addListeners(reporter, config); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             RunContext context(config, std::move(reporter)); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             Totals totals; | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             context.testGroupStarting(config->name(), 1, 1); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             TestSpec testSpec = config->testSpec(); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             auto const& allTestCases = getAllTestCasesSorted(*config); | 
					
						
							|  |  |  |             for (auto const& testCase : allTestCases) { | 
					
						
							|  |  |  |                 if (!context.aborting() && matchTest(testCase, testSpec, *config)) | 
					
						
							|  |  |  |                     totals += context.runTest(testCase); | 
					
						
							|  |  |  |                 else | 
					
						
							|  |  |  |                     context.reporter().skipTest(testCase); | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-09 01:31:19 +02:00
										 |  |  |             if (config->warnAboutNoTests() && totals.testCases.total() == 0) { | 
					
						
							|  |  |  |                 ReusableStringStream testConfig; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 bool first = true; | 
					
						
							|  |  |  |                 for (const auto& input : config->getTestsOrTags()) { | 
					
						
							|  |  |  |                     if (!first) { testConfig << ' '; } | 
					
						
							|  |  |  |                     first = false; | 
					
						
							|  |  |  |                     testConfig << input; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 context.reporter().noMatchingTestCases(testConfig.str()); | 
					
						
							|  |  |  |                 totals.error = -1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |             context.testGroupEnded(config->name(), totals, 1, 1); | 
					
						
							|  |  |  |             return totals; | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |         void applyFilenamesAsTags(Catch::IConfig const& config) { | 
					
						
							|  |  |  |             auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config)); | 
					
						
							|  |  |  |             for (auto& testCase : tests) { | 
					
						
							|  |  |  |                 auto tags = testCase.tags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 std::string filename = testCase.lineInfo.file; | 
					
						
							|  |  |  |                 auto lastSlash = filename.find_last_of("\\/"); | 
					
						
							|  |  |  |                 if (lastSlash != std::string::npos) { | 
					
						
							|  |  |  |                     filename.erase(0, lastSlash); | 
					
						
							|  |  |  |                     filename[0] = '#'; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 auto lastDot = filename.find_last_of('.'); | 
					
						
							|  |  |  |                 if (lastDot != std::string::npos) { | 
					
						
							|  |  |  |                     filename.erase(lastDot); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 tags.push_back(std::move(filename)); | 
					
						
							|  |  |  |                 setTags(testCase, tags); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-05 23:26:21 +00:00
										 |  |  |     } // anon namespace
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Session::Session() { | 
					
						
							|  |  |  |         static bool alreadyInstantiated = false; | 
					
						
							| 
									
										
										
										
											2017-11-02 17:57:52 +00:00
										 |  |  |         if( alreadyInstantiated ) { | 
					
						
							|  |  |  |             try         { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } | 
					
						
							|  |  |  |             catch(...)  { getMutableRegistryHub().registerStartupException(); } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); | 
					
						
							|  |  |  |         if ( !exceptions.empty() ) { | 
					
						
							|  |  |  |             m_startupExceptions = true; | 
					
						
							|  |  |  |             Colour colourGuard( Colour::Red ); | 
					
						
							|  |  |  |             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() << Column( ex.what() ).indent(2) << '\n'; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  |         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 Catch 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* argv[] ) { | 
					
						
							| 
									
										
										
										
											2017-11-02 17:57:52 +00:00
										 |  |  |         if( m_startupExceptions ) | 
					
						
							|  |  |  |             return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  |         auto result = m_cli.parse( clara::Args( argc, argv ) ); | 
					
						
							|  |  |  |         if( !result ) { | 
					
						
							|  |  |  |             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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void Session::useConfigData( ConfigData const& configData ) { | 
					
						
							|  |  |  |         m_configData = configData; | 
					
						
							|  |  |  |         m_config.reset(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int Session::run( int argc, char* argv[] ) { | 
					
						
							| 
									
										
										
										
											2017-11-02 17:57:52 +00:00
										 |  |  |         if( m_startupExceptions ) | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  |             return 1; | 
					
						
							|  |  |  |         int returnCode = applyCommandLine( argc, argv ); | 
					
						
							|  |  |  |         if( returnCode == 0 ) | 
					
						
							|  |  |  |             returnCode = run(); | 
					
						
							|  |  |  |         return returnCode; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if defined(WIN32) && defined(UNICODE)
 | 
					
						
							|  |  |  |     int Session::run( int argc, wchar_t* 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 = run( argc, utf8Argv ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for ( int i = 0; i < argc; ++i ) | 
					
						
							|  |  |  |             delete [] utf8Argv[ i ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         delete [] utf8Argv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return returnCode; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     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() { | 
					
						
							| 
									
										
										
										
											2017-11-02 17:57:52 +00:00
										 |  |  |         if( m_startupExceptions ) | 
					
						
							|  |  |  |             return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  |         if( m_configData.showHelp || m_configData.libIdentify ) | 
					
						
							|  |  |  |             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 ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-09 01:31:19 +02:00
										 |  |  |             auto totals = runTests( m_config ); | 
					
						
							| 
									
										
										
										
											2018-01-12 11:49:48 +01:00
										 |  |  |             // 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
 | 
					
						
							| 
									
										
										
										
											2018-02-09 01:31:19 +02:00
										 |  |  |             return (std::min)( { MaxExitCode, totals.error, static_cast<int>( totals.assertions.failed ) } ); | 
					
						
							| 
									
										
										
										
											2017-08-31 10:31:52 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         catch( std::exception& ex ) { | 
					
						
							|  |  |  |             Catch::cerr() << ex.what() << std::endl; | 
					
						
							|  |  |  |             return MaxExitCode; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // end namespace Catch
 |