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 9 #include "catch_commandline.h" 10 11 #include "catch_string_manip.h" 12 13 #include "catch_interfaces_registry_hub.h" 14 #include "catch_interfaces_reporter.h" 15 16 #include <fstream> 17 #include <ctime> 18 19 namespace Catch { 20 makeCommandLineParser(ConfigData & config)21 clara::Parser makeCommandLineParser( ConfigData& config ) { 22 23 using namespace clara; 24 25 auto const setWarning = [&]( std::string const& warning ) { 26 auto warningSet = [&]() { 27 if( warning == "NoAssertions" ) 28 return WarnAbout::NoAssertions; 29 30 if ( warning == "NoTests" ) 31 return WarnAbout::NoTests; 32 33 return WarnAbout::Nothing; 34 }(); 35 36 if (warningSet == WarnAbout::Nothing) 37 return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); 38 config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet ); 39 return ParserResult::ok( ParseResultType::Matched ); 40 }; 41 auto const loadTestNamesFromFile = [&]( std::string const& filename ) { 42 std::ifstream f( filename.c_str() ); 43 if( !f.is_open() ) 44 return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); 45 46 std::string line; 47 while( std::getline( f, line ) ) { 48 line = trim(line); 49 if( !line.empty() && !startsWith( line, '#' ) ) { 50 if( !startsWith( line, '"' ) ) 51 line = '"' + line + '"'; 52 config.testsOrTags.push_back( line + ',' ); 53 } 54 } 55 return ParserResult::ok( ParseResultType::Matched ); 56 }; 57 auto const setTestOrder = [&]( std::string const& order ) { 58 if( startsWith( "declared", order ) ) 59 config.runOrder = RunTests::InDeclarationOrder; 60 else if( startsWith( "lexical", order ) ) 61 config.runOrder = RunTests::InLexicographicalOrder; 62 else if( startsWith( "random", order ) ) 63 config.runOrder = RunTests::InRandomOrder; 64 else 65 return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); 66 return ParserResult::ok( ParseResultType::Matched ); 67 }; 68 auto const setRngSeed = [&]( std::string const& seed ) { 69 if( seed != "time" ) 70 return clara::detail::convertInto( seed, config.rngSeed ); 71 config.rngSeed = static_cast<unsigned int>( std::time(nullptr) ); 72 return ParserResult::ok( ParseResultType::Matched ); 73 }; 74 auto const setColourUsage = [&]( std::string const& useColour ) { 75 auto mode = toLower( useColour ); 76 77 if( mode == "yes" ) 78 config.useColour = UseColour::Yes; 79 else if( mode == "no" ) 80 config.useColour = UseColour::No; 81 else if( mode == "auto" ) 82 config.useColour = UseColour::Auto; 83 else 84 return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); 85 return ParserResult::ok( ParseResultType::Matched ); 86 }; 87 auto const setWaitForKeypress = [&]( std::string const& keypress ) { 88 auto keypressLc = toLower( keypress ); 89 if( keypressLc == "start" ) 90 config.waitForKeypress = WaitForKeypress::BeforeStart; 91 else if( keypressLc == "exit" ) 92 config.waitForKeypress = WaitForKeypress::BeforeExit; 93 else if( keypressLc == "both" ) 94 config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; 95 else 96 return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); 97 return ParserResult::ok( ParseResultType::Matched ); 98 }; 99 auto const setVerbosity = [&]( std::string const& verbosity ) { 100 auto lcVerbosity = toLower( verbosity ); 101 if( lcVerbosity == "quiet" ) 102 config.verbosity = Verbosity::Quiet; 103 else if( lcVerbosity == "normal" ) 104 config.verbosity = Verbosity::Normal; 105 else if( lcVerbosity == "high" ) 106 config.verbosity = Verbosity::High; 107 else 108 return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); 109 return ParserResult::ok( ParseResultType::Matched ); 110 }; 111 auto const setReporter = [&]( std::string const& reporter ) { 112 IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); 113 114 auto lcReporter = toLower( reporter ); 115 auto result = factories.find( lcReporter ); 116 117 if( factories.end() != result ) 118 config.reporterName = lcReporter; 119 else 120 return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" ); 121 return ParserResult::ok( ParseResultType::Matched ); 122 }; 123 124 auto cli 125 = ExeName( config.processName ) 126 | Help( config.showHelp ) 127 | Opt( config.listTests ) 128 ["-l"]["--list-tests"] 129 ( "list all/matching test cases" ) 130 | Opt( config.listTags ) 131 ["-t"]["--list-tags"] 132 ( "list all/matching tags" ) 133 | Opt( config.showSuccessfulTests ) 134 ["-s"]["--success"] 135 ( "include successful tests in output" ) 136 | Opt( config.shouldDebugBreak ) 137 ["-b"]["--break"] 138 ( "break into debugger on failure" ) 139 | Opt( config.noThrow ) 140 ["-e"]["--nothrow"] 141 ( "skip exception tests" ) 142 | Opt( config.showInvisibles ) 143 ["-i"]["--invisibles"] 144 ( "show invisibles (tabs, newlines)" ) 145 | Opt( config.outputFilename, "filename" ) 146 ["-o"]["--out"] 147 ( "output filename" ) 148 | Opt( setReporter, "name" ) 149 ["-r"]["--reporter"] 150 ( "reporter to use (defaults to console)" ) 151 | Opt( config.name, "name" ) 152 ["-n"]["--name"] 153 ( "suite name" ) 154 | Opt( [&]( bool ){ config.abortAfter = 1; } ) 155 ["-a"]["--abort"] 156 ( "abort at first failure" ) 157 | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) 158 ["-x"]["--abortx"] 159 ( "abort after x failures" ) 160 | Opt( setWarning, "warning name" ) 161 ["-w"]["--warn"] 162 ( "enable warnings" ) 163 | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) 164 ["-d"]["--durations"] 165 ( "show test durations" ) 166 | Opt( loadTestNamesFromFile, "filename" ) 167 ["-f"]["--input-file"] 168 ( "load test names to run from a file" ) 169 | Opt( config.filenamesAsTags ) 170 ["-#"]["--filenames-as-tags"] 171 ( "adds a tag for the filename" ) 172 | Opt( config.sectionsToRun, "section name" ) 173 ["-c"]["--section"] 174 ( "specify section to run" ) 175 | Opt( setVerbosity, "quiet|normal|high" ) 176 ["-v"]["--verbosity"] 177 ( "set output verbosity" ) 178 | Opt( config.listTestNamesOnly ) 179 ["--list-test-names-only"] 180 ( "list all/matching test cases names only" ) 181 | Opt( config.listReporters ) 182 ["--list-reporters"] 183 ( "list all reporters" ) 184 | Opt( setTestOrder, "decl|lex|rand" ) 185 ["--order"] 186 ( "test case order (defaults to decl)" ) 187 | Opt( setRngSeed, "'time'|number" ) 188 ["--rng-seed"] 189 ( "set a specific seed for random numbers" ) 190 | Opt( setColourUsage, "yes|no" ) 191 ["--use-colour"] 192 ( "should output be colourised" ) 193 | Opt( config.libIdentify ) 194 ["--libidentify"] 195 ( "report name and version according to libidentify standard" ) 196 | Opt( setWaitForKeypress, "start|exit|both" ) 197 ["--wait-for-keypress"] 198 ( "waits for a keypress before exiting" ) 199 | Opt( config.benchmarkResolutionMultiple, "multiplier" ) 200 ["--benchmark-resolution-multiple"] 201 ( "multiple of clock resolution to run benchmarks" ) 202 203 | Arg( config.testsOrTags, "test name|pattern|tags" ) 204 ( "which test or tests to use" ); 205 206 return cli; 207 } 208 209 } // end namespace Catch 210