1 /* 2 * Created by Phil on 31/10/2010. 3 * Copyright 2010 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 #ifndef TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED 10 11 #include "internal/catch_commandline.hpp" 12 #include "internal/catch_list.hpp" 13 #include "internal/catch_run_context.hpp" 14 #include "internal/catch_test_spec.hpp" 15 #include "internal/catch_version.h" 16 #include "internal/catch_text.h" 17 18 #include <fstream> 19 #include <stdlib.h> 20 #include <limits> 21 22 namespace Catch { 23 createReporter(std::string const & reporterName,Ptr<Config> const & config)24 Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) { 25 Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); 26 if( !reporter ) { 27 std::ostringstream oss; 28 oss << "No reporter registered with name: '" << reporterName << "'"; 29 throw std::domain_error( oss.str() ); 30 } 31 return reporter; 32 } 33 34 #if !defined(CATCH_CONFIG_DEFAULT_REPORTER) 35 #define CATCH_CONFIG_DEFAULT_REPORTER "console" 36 #endif 37 makeReporter(Ptr<Config> const & config)38 Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) { 39 std::vector<std::string> reporters = config->getReporterNames(); 40 if( reporters.empty() ) 41 reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER ); 42 43 Ptr<IStreamingReporter> reporter; 44 for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end(); 45 it != itEnd; 46 ++it ) 47 reporter = addReporter( reporter, createReporter( *it, config ) ); 48 return reporter; 49 } addListeners(Ptr<IConfig const> const & config,Ptr<IStreamingReporter> reporters)50 Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) { 51 IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); 52 for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); 53 it != itEnd; 54 ++it ) 55 reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); 56 return reporters; 57 } 58 59 runTests(Ptr<Config> const & config)60 Totals runTests( Ptr<Config> const& config ) { 61 62 Ptr<IConfig const> iconfig = config.get(); 63 64 Ptr<IStreamingReporter> reporter = makeReporter( config ); 65 reporter = addListeners( iconfig, reporter ); 66 67 RunContext context( iconfig, reporter ); 68 69 Totals totals; 70 71 context.testGroupStarting( config->name(), 1, 1 ); 72 73 TestSpec testSpec = config->testSpec(); 74 if( !testSpec.hasFilters() ) 75 testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests 76 77 std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig ); 78 for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); 79 it != itEnd; 80 ++it ) { 81 if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) 82 totals += context.runTest( *it ); 83 else 84 reporter->skipTest( *it ); 85 } 86 87 context.testGroupEnded( iconfig->name(), totals, 1, 1 ); 88 return totals; 89 } 90 applyFilenamesAsTags(IConfig const & config)91 void applyFilenamesAsTags( IConfig const& config ) { 92 std::vector<TestCase> const& tests = getAllTestCasesSorted( config ); 93 for(std::size_t i = 0; i < tests.size(); ++i ) { 94 TestCase& test = const_cast<TestCase&>( tests[i] ); 95 std::set<std::string> tags = test.tags; 96 97 std::string filename = test.lineInfo.file; 98 std::string::size_type lastSlash = filename.find_last_of( "\\/" ); 99 if( lastSlash != std::string::npos ) 100 filename = filename.substr( lastSlash+1 ); 101 102 std::string::size_type lastDot = filename.find_last_of( '.' ); 103 if( lastDot != std::string::npos ) 104 filename = filename.substr( 0, lastDot ); 105 106 tags.insert( '#' + filename ); 107 setTags( test, tags ); 108 } 109 } 110 111 class Session : NonCopyable { 112 static bool alreadyInstantiated; 113 114 public: 115 116 struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; 117 Session()118 Session() 119 : m_cli( makeCommandLineParser() ) { 120 if( alreadyInstantiated ) { 121 std::string msg = "Only one instance of Catch::Session can ever be used"; 122 Catch::cerr() << msg << std::endl; 123 throw std::logic_error( msg ); 124 } 125 alreadyInstantiated = true; 126 } ~Session()127 ~Session() { 128 Catch::cleanUp(); 129 } 130 showHelp(std::string const & processName)131 void showHelp( std::string const& processName ) { 132 Catch::cout() << "\nCatch v" << libraryVersion() << "\n"; 133 134 m_cli.usage( Catch::cout(), processName ); 135 Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; 136 } libIdentify()137 void libIdentify() { 138 Catch::cout() 139 << std::left << std::setw(16) << "description: " << "A Catch test executable\n" 140 << std::left << std::setw(16) << "category: " << "testframework\n" 141 << std::left << std::setw(16) << "framework: " << "Catch Test\n" 142 << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; 143 } 144 applyCommandLine(int argc,char const * const * const argv,OnUnusedOptions::DoWhat unusedOptionBehaviour=OnUnusedOptions::Fail)145 int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { 146 try { 147 m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); 148 m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); 149 if( m_configData.showHelp ) 150 showHelp( m_configData.processName ); 151 if( m_configData.libIdentify ) 152 libIdentify(); 153 m_config.reset(); 154 } 155 catch( std::exception& ex ) { 156 { 157 Colour colourGuard( Colour::Red ); 158 Catch::cerr() 159 << "\nError(s) in input:\n" 160 << Text( ex.what(), TextAttributes().setIndent(2) ) 161 << "\n\n"; 162 } 163 m_cli.usage( Catch::cout(), m_configData.processName ); 164 return (std::numeric_limits<int>::max)(); 165 } 166 return 0; 167 } 168 useConfigData(ConfigData const & _configData)169 void useConfigData( ConfigData const& _configData ) { 170 m_configData = _configData; 171 m_config.reset(); 172 } 173 run(int argc,char const * const * const argv)174 int run( int argc, char const* const* const argv ) { 175 176 int returnCode = applyCommandLine( argc, argv ); 177 if( returnCode == 0 ) 178 returnCode = run(); 179 return returnCode; 180 } 181 182 #if defined(WIN32) && defined(UNICODE) run(int argc,wchar_t const * const * const argv)183 int run( int argc, wchar_t const* const* const argv ) { 184 185 char **utf8Argv = new char *[ argc ]; 186 187 for ( int i = 0; i < argc; ++i ) { 188 int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); 189 190 utf8Argv[ i ] = new char[ bufSize ]; 191 192 WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); 193 } 194 195 int returnCode = applyCommandLine( argc, utf8Argv ); 196 if( returnCode == 0 ) 197 returnCode = run(); 198 199 for ( int i = 0; i < argc; ++i ) 200 delete [] utf8Argv[ i ]; 201 202 delete [] utf8Argv; 203 204 return returnCode; 205 } 206 #endif 207 run()208 int run() { 209 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { 210 Catch::cout() << "...waiting for enter/ return before starting" << std::endl; 211 static_cast<void>(std::getchar()); 212 } 213 int exitCode = runInternal(); 214 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { 215 Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; 216 static_cast<void>(std::getchar()); 217 } 218 return exitCode; 219 } 220 cli() const221 Clara::CommandLine<ConfigData> const& cli() const { 222 return m_cli; 223 } unusedTokens() const224 std::vector<Clara::Parser::Token> const& unusedTokens() const { 225 return m_unusedTokens; 226 } configData()227 ConfigData& configData() { 228 return m_configData; 229 } config()230 Config& config() { 231 if( !m_config ) 232 m_config = new Config( m_configData ); 233 return *m_config; 234 } 235 private: 236 runInternal()237 int runInternal() { 238 if( m_configData.showHelp || m_configData.libIdentify ) 239 return 0; 240 241 try 242 { 243 config(); // Force config to be constructed 244 245 seedRng( *m_config ); 246 247 if( m_configData.filenamesAsTags ) 248 applyFilenamesAsTags( *m_config ); 249 250 // Handle list request 251 if( Option<std::size_t> listed = list( config() ) ) 252 return static_cast<int>( *listed ); 253 254 return static_cast<int>( runTests( m_config ).assertions.failed ); 255 } 256 catch( std::exception& ex ) { 257 Catch::cerr() << ex.what() << std::endl; 258 return (std::numeric_limits<int>::max)(); 259 } 260 } 261 262 Clara::CommandLine<ConfigData> m_cli; 263 std::vector<Clara::Parser::Token> m_unusedTokens; 264 ConfigData m_configData; 265 Ptr<Config> m_config; 266 }; 267 268 bool Session::alreadyInstantiated = false; 269 270 } // end namespace Catch 271 272 #endif // TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED 273