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