1 /* 2 * Created by Phil on 02/11/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_COMMANDLINE_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED 10 11 #include "catch_config.hpp" 12 #include "catch_common.h" 13 #include "catch_clara.h" 14 15 #include <fstream> 16 #include <ctime> 17 18 namespace Catch { 19 abortAfterFirst(ConfigData & config)20 inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } abortAfterX(ConfigData & config,int x)21 inline void abortAfterX( ConfigData& config, int x ) { 22 if( x < 1 ) 23 throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); 24 config.abortAfter = x; 25 } addTestOrTags(ConfigData & config,std::string const & _testSpec)26 inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } addSectionToRun(ConfigData & config,std::string const & sectionName)27 inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } addReporterName(ConfigData & config,std::string const & _reporterName)28 inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } 29 addWarning(ConfigData & config,std::string const & _warning)30 inline void addWarning( ConfigData& config, std::string const& _warning ) { 31 if( _warning == "NoAssertions" ) 32 config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions ); 33 else 34 throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); 35 } setOrder(ConfigData & config,std::string const & order)36 inline void setOrder( ConfigData& config, std::string const& order ) { 37 if( startsWith( "declared", order ) ) 38 config.runOrder = RunTests::InDeclarationOrder; 39 else if( startsWith( "lexical", order ) ) 40 config.runOrder = RunTests::InLexicographicalOrder; 41 else if( startsWith( "random", order ) ) 42 config.runOrder = RunTests::InRandomOrder; 43 else 44 throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); 45 } setRngSeed(ConfigData & config,std::string const & seed)46 inline void setRngSeed( ConfigData& config, std::string const& seed ) { 47 if( seed == "time" ) { 48 config.rngSeed = static_cast<unsigned int>( std::time(0) ); 49 } 50 else { 51 std::stringstream ss; 52 ss << seed; 53 ss >> config.rngSeed; 54 if( ss.fail() ) 55 throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); 56 } 57 } setVerbosity(ConfigData & config,int level)58 inline void setVerbosity( ConfigData& config, int level ) { 59 // !TBD: accept strings? 60 config.verbosity = static_cast<Verbosity::Level>( level ); 61 } setShowDurations(ConfigData & config,bool _showDurations)62 inline void setShowDurations( ConfigData& config, bool _showDurations ) { 63 config.showDurations = _showDurations 64 ? ShowDurations::Always 65 : ShowDurations::Never; 66 } setUseColour(ConfigData & config,std::string const & value)67 inline void setUseColour( ConfigData& config, std::string const& value ) { 68 std::string mode = toLower( value ); 69 70 if( mode == "yes" ) 71 config.useColour = UseColour::Yes; 72 else if( mode == "no" ) 73 config.useColour = UseColour::No; 74 else if( mode == "auto" ) 75 config.useColour = UseColour::Auto; 76 else 77 throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); 78 } setWaitForKeypress(ConfigData & config,std::string const & keypress)79 inline void setWaitForKeypress( ConfigData& config, std::string const& keypress ) { 80 std::string keypressLc = toLower( keypress ); 81 if( keypressLc == "start" ) 82 config.waitForKeypress = WaitForKeypress::BeforeStart; 83 else if( keypressLc == "exit" ) 84 config.waitForKeypress = WaitForKeypress::BeforeExit; 85 else if( keypressLc == "both" ) 86 config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; 87 else 88 throw std::runtime_error( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); 89 }; 90 91 forceColour(ConfigData & config)92 inline void forceColour( ConfigData& config ) { 93 config.useColour = UseColour::Yes; 94 } loadTestNamesFromFile(ConfigData & config,std::string const & _filename)95 inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { 96 std::ifstream f( _filename.c_str() ); 97 if( !f.is_open() ) 98 throw std::domain_error( "Unable to load input file: " + _filename ); 99 100 std::string line; 101 while( std::getline( f, line ) ) { 102 line = trim(line); 103 if( !line.empty() && !startsWith( line, '#' ) ) { 104 if( !startsWith( line, '"' ) ) 105 line = '"' + line + '"'; 106 addTestOrTags( config, line + ',' ); 107 } 108 } 109 } 110 makeCommandLineParser()111 inline Clara::CommandLine<ConfigData> makeCommandLineParser() { 112 113 using namespace Clara; 114 CommandLine<ConfigData> cli; 115 116 cli.bindProcessName( &ConfigData::processName ); 117 118 cli["-?"]["-h"]["--help"] 119 .describe( "display usage information" ) 120 .bind( &ConfigData::showHelp ); 121 122 cli["-l"]["--list-tests"] 123 .describe( "list all/matching test cases" ) 124 .bind( &ConfigData::listTests ); 125 126 cli["-t"]["--list-tags"] 127 .describe( "list all/matching tags" ) 128 .bind( &ConfigData::listTags ); 129 130 cli["-s"]["--success"] 131 .describe( "include successful tests in output" ) 132 .bind( &ConfigData::showSuccessfulTests ); 133 134 cli["-b"]["--break"] 135 .describe( "break into debugger on failure" ) 136 .bind( &ConfigData::shouldDebugBreak ); 137 138 cli["-e"]["--nothrow"] 139 .describe( "skip exception tests" ) 140 .bind( &ConfigData::noThrow ); 141 142 cli["-i"]["--invisibles"] 143 .describe( "show invisibles (tabs, newlines)" ) 144 .bind( &ConfigData::showInvisibles ); 145 146 cli["-o"]["--out"] 147 .describe( "output filename" ) 148 .bind( &ConfigData::outputFilename, "filename" ); 149 150 cli["-r"]["--reporter"] 151 // .placeholder( "name[:filename]" ) 152 .describe( "reporter to use (defaults to console)" ) 153 .bind( &addReporterName, "name" ); 154 155 cli["-n"]["--name"] 156 .describe( "suite name" ) 157 .bind( &ConfigData::name, "name" ); 158 159 cli["-a"]["--abort"] 160 .describe( "abort at first failure" ) 161 .bind( &abortAfterFirst ); 162 163 cli["-x"]["--abortx"] 164 .describe( "abort after x failures" ) 165 .bind( &abortAfterX, "no. failures" ); 166 167 cli["-w"]["--warn"] 168 .describe( "enable warnings" ) 169 .bind( &addWarning, "warning name" ); 170 171 // - needs updating if reinstated 172 // cli.into( &setVerbosity ) 173 // .describe( "level of verbosity (0=no output)" ) 174 // .shortOpt( "v") 175 // .longOpt( "verbosity" ) 176 // .placeholder( "level" ); 177 178 cli[_] 179 .describe( "which test or tests to use" ) 180 .bind( &addTestOrTags, "test name, pattern or tags" ); 181 182 cli["-d"]["--durations"] 183 .describe( "show test durations" ) 184 .bind( &setShowDurations, "yes|no" ); 185 186 cli["-f"]["--input-file"] 187 .describe( "load test names to run from a file" ) 188 .bind( &loadTestNamesFromFile, "filename" ); 189 190 cli["-#"]["--filenames-as-tags"] 191 .describe( "adds a tag for the filename" ) 192 .bind( &ConfigData::filenamesAsTags ); 193 194 cli["-c"]["--section"] 195 .describe( "specify section to run" ) 196 .bind( &addSectionToRun, "section name" ); 197 198 // Less common commands which don't have a short form 199 cli["--list-test-names-only"] 200 .describe( "list all/matching test cases names only" ) 201 .bind( &ConfigData::listTestNamesOnly ); 202 203 cli["--list-extra-info"] 204 .describe( "list all/matching test cases with more info" ) 205 .bind( &ConfigData::listExtraInfo ); 206 207 cli["--list-reporters"] 208 .describe( "list all reporters" ) 209 .bind( &ConfigData::listReporters ); 210 211 cli["--order"] 212 .describe( "test case order (defaults to decl)" ) 213 .bind( &setOrder, "decl|lex|rand" ); 214 215 cli["--rng-seed"] 216 .describe( "set a specific seed for random numbers" ) 217 .bind( &setRngSeed, "'time'|number" ); 218 219 cli["--force-colour"] 220 .describe( "force colourised output (deprecated)" ) 221 .bind( &forceColour ); 222 223 cli["--use-colour"] 224 .describe( "should output be colourised" ) 225 .bind( &setUseColour, "yes|no" ); 226 227 cli["--libidentify"] 228 .describe( "report name and version according to libidentify standard" ) 229 .bind( &ConfigData::libIdentify ); 230 231 cli["--wait-for-keypress"] 232 .describe( "waits for a keypress before exiting" ) 233 .bind( &setWaitForKeypress, "start|exit|both" ); 234 235 return cli; 236 } 237 238 } // end namespace Catch 239 240 #endif // TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED 241