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                         config.testsOrTags.emplace_back( "," );
54                     }
55                 }
56                 //Remove comma in the end
57                 if(!config.testsOrTags.empty())
58                     config.testsOrTags.erase( config.testsOrTags.end()-1 );
59 
60                 return ParserResult::ok( ParseResultType::Matched );
61             };
62         auto const setTestOrder = [&]( std::string const& order ) {
63                 if( startsWith( "declared", order ) )
64                     config.runOrder = RunTests::InDeclarationOrder;
65                 else if( startsWith( "lexical", order ) )
66                     config.runOrder = RunTests::InLexicographicalOrder;
67                 else if( startsWith( "random", order ) )
68                     config.runOrder = RunTests::InRandomOrder;
69                 else
70                     return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
71                 return ParserResult::ok( ParseResultType::Matched );
72             };
73         auto const setRngSeed = [&]( std::string const& seed ) {
74                 if( seed != "time" )
75                     return clara::detail::convertInto( seed, config.rngSeed );
76                 config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
77                 return ParserResult::ok( ParseResultType::Matched );
78             };
79         auto const setColourUsage = [&]( std::string const& useColour ) {
80                     auto mode = toLower( useColour );
81 
82                     if( mode == "yes" )
83                         config.useColour = UseColour::Yes;
84                     else if( mode == "no" )
85                         config.useColour = UseColour::No;
86                     else if( mode == "auto" )
87                         config.useColour = UseColour::Auto;
88                     else
89                         return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" );
90                 return ParserResult::ok( ParseResultType::Matched );
91             };
92         auto const setWaitForKeypress = [&]( std::string const& keypress ) {
93                 auto keypressLc = toLower( keypress );
94                 if (keypressLc == "never")
95                     config.waitForKeypress = WaitForKeypress::Never;
96                 else if( keypressLc == "start" )
97                     config.waitForKeypress = WaitForKeypress::BeforeStart;
98                 else if( keypressLc == "exit" )
99                     config.waitForKeypress = WaitForKeypress::BeforeExit;
100                 else if( keypressLc == "both" )
101                     config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
102                 else
103                     return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
104             return ParserResult::ok( ParseResultType::Matched );
105             };
106         auto const setVerbosity = [&]( std::string const& verbosity ) {
107             auto lcVerbosity = toLower( verbosity );
108             if( lcVerbosity == "quiet" )
109                 config.verbosity = Verbosity::Quiet;
110             else if( lcVerbosity == "normal" )
111                 config.verbosity = Verbosity::Normal;
112             else if( lcVerbosity == "high" )
113                 config.verbosity = Verbosity::High;
114             else
115                 return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );
116             return ParserResult::ok( ParseResultType::Matched );
117         };
118         auto const setReporter = [&]( std::string const& reporter ) {
119             IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
120 
121             auto lcReporter = toLower( reporter );
122             auto result = factories.find( lcReporter );
123 
124             if( factories.end() != result )
125                 config.reporterName = lcReporter;
126             else
127                 return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" );
128             return ParserResult::ok( ParseResultType::Matched );
129         };
130 
131         auto cli
132             = ExeName( config.processName )
133             | Help( config.showHelp )
134             | Opt( config.listTests )
135                 ["-l"]["--list-tests"]
136                 ( "list all/matching test cases" )
137             | Opt( config.listTags )
138                 ["-t"]["--list-tags"]
139                 ( "list all/matching tags" )
140             | Opt( config.showSuccessfulTests )
141                 ["-s"]["--success"]
142                 ( "include successful tests in output" )
143             | Opt( config.shouldDebugBreak )
144                 ["-b"]["--break"]
145                 ( "break into debugger on failure" )
146             | Opt( config.noThrow )
147                 ["-e"]["--nothrow"]
148                 ( "skip exception tests" )
149             | Opt( config.showInvisibles )
150                 ["-i"]["--invisibles"]
151                 ( "show invisibles (tabs, newlines)" )
152             | Opt( config.outputFilename, "filename" )
153                 ["-o"]["--out"]
154                 ( "output filename" )
155             | Opt( setReporter, "name" )
156                 ["-r"]["--reporter"]
157                 ( "reporter to use (defaults to console)" )
158             | Opt( config.name, "name" )
159                 ["-n"]["--name"]
160                 ( "suite name" )
161             | Opt( [&]( bool ){ config.abortAfter = 1; } )
162                 ["-a"]["--abort"]
163                 ( "abort at first failure" )
164             | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
165                 ["-x"]["--abortx"]
166                 ( "abort after x failures" )
167             | Opt( setWarning, "warning name" )
168                 ["-w"]["--warn"]
169                 ( "enable warnings" )
170             | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
171                 ["-d"]["--durations"]
172                 ( "show test durations" )
173             | Opt( config.minDuration, "seconds" )
174                 ["-D"]["--min-duration"]
175                 ( "show test durations for tests taking at least the given number of seconds" )
176             | Opt( loadTestNamesFromFile, "filename" )
177                 ["-f"]["--input-file"]
178                 ( "load test names to run from a file" )
179             | Opt( config.filenamesAsTags )
180                 ["-#"]["--filenames-as-tags"]
181                 ( "adds a tag for the filename" )
182             | Opt( config.sectionsToRun, "section name" )
183                 ["-c"]["--section"]
184                 ( "specify section to run" )
185             | Opt( setVerbosity, "quiet|normal|high" )
186                 ["-v"]["--verbosity"]
187                 ( "set output verbosity" )
188             | Opt( config.listTestNamesOnly )
189                 ["--list-test-names-only"]
190                 ( "list all/matching test cases names only" )
191             | Opt( config.listReporters )
192                 ["--list-reporters"]
193                 ( "list all reporters" )
194             | Opt( setTestOrder, "decl|lex|rand" )
195                 ["--order"]
196                 ( "test case order (defaults to decl)" )
197             | Opt( setRngSeed, "'time'|number" )
198                 ["--rng-seed"]
199                 ( "set a specific seed for random numbers" )
200             | Opt( setColourUsage, "yes|no" )
201                 ["--use-colour"]
202                 ( "should output be colourised" )
203             | Opt( config.libIdentify )
204                 ["--libidentify"]
205                 ( "report name and version according to libidentify standard" )
206             | Opt( setWaitForKeypress, "never|start|exit|both" )
207                 ["--wait-for-keypress"]
208                 ( "waits for a keypress before exiting" )
209             | Opt( config.benchmarkSamples, "samples" )
210                 ["--benchmark-samples"]
211                 ( "number of samples to collect (default: 100)" )
212             | Opt( config.benchmarkResamples, "resamples" )
213                 ["--benchmark-resamples"]
214                 ( "number of resamples for the bootstrap (default: 100000)" )
215             | Opt( config.benchmarkConfidenceInterval, "confidence interval" )
216                 ["--benchmark-confidence-interval"]
217                 ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
218             | Opt( config.benchmarkNoAnalysis )
219                 ["--benchmark-no-analysis"]
220                 ( "perform only measurements; do not perform any analysis" )
221             | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
222                 ["--benchmark-warmup-time"]
223                 ( "amount of time in milliseconds spent on warming up each test (default: 100)" )
224             | Arg( config.testsOrTags, "test name|pattern|tags" )
225                 ( "which test or tests to use" );
226 
227         return cli;
228     }
229 
230 } // end namespace Catch
231