mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	Some refactoring of Clara impl and experimental string based interface
This commit is contained in:
		| @@ -14,51 +14,13 @@ | ||||
|  | ||||
| namespace Clara { | ||||
|  | ||||
| template<typename T> struct RemoveConstRef{ typedef T type; }; | ||||
| template<typename T> struct RemoveConstRef<T&>{ typedef T type; }; | ||||
| template<typename T> struct RemoveConstRef<T const&>{ typedef T type; }; | ||||
| template<typename T> struct RemoveConstRef<T const>{ typedef T type; }; | ||||
|  | ||||
| using namespace Catch; | ||||
|  | ||||
| class ArgData { | ||||
| public: | ||||
|     ArgData( std::string const& _arg ) : m_weight( 2 ) | ||||
|     { | ||||
|         std::size_t first = _arg.find( '<' ); | ||||
|         std::size_t last = _arg.find_last_of( '>' ); | ||||
|         if( first == std::string::npos || last == std::string::npos || last <= first+1 ) | ||||
|             throw std::logic_error( "Argument must contain a name in angle brackets but it was: " + _arg ); | ||||
|         m_prefix = _arg.substr( 0, first ); | ||||
|         m_postfix = _arg.substr( last+1 ); | ||||
|         m_name = _arg.substr( first+1, last-first-1 ); | ||||
|         if( !m_prefix.empty() ) | ||||
|             --m_weight; | ||||
|         if( !m_postfix.empty() ) | ||||
|             --m_weight; | ||||
|     } | ||||
|     std::string const& name() const { return m_name; } | ||||
|     std::string const& prefix() const { return m_prefix; } | ||||
|     std::string const& postfix() const { return m_postfix; } | ||||
|  | ||||
|     bool isMatch( std::string const& _arg ) const { | ||||
|         return startsWith( _arg, m_prefix ) && endsWith( _arg, m_postfix ); | ||||
|     } | ||||
|     std::string strip( std::string const& _arg ) const { | ||||
|         return _arg.substr( m_prefix.size(), | ||||
|                             _arg.size() - m_prefix.size() - m_postfix.size() ); | ||||
|     } | ||||
|     bool operator < ( ArgData const& _other ) const { | ||||
|         return m_weight < _other.m_weight; | ||||
|     } | ||||
|      | ||||
|     friend std::ostream& operator << ( std::ostream& os, ArgData const& _arg ) { | ||||
|         os << _arg.m_prefix << "<" << _arg.m_name << ">" << _arg.m_postfix; | ||||
|         return os; | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     std::string m_prefix; | ||||
|     std::string m_postfix; | ||||
|     std::string m_name; | ||||
|     int m_weight; | ||||
| }; | ||||
|  | ||||
| template<typename T> | ||||
| bool convertInto( std::string const& _source, T& _dest ) { | ||||
|     std::stringstream ss; | ||||
| @@ -70,75 +32,88 @@ inline bool convertInto( std::string const& _source, std::string& _dest ) { | ||||
|     _dest = _source; | ||||
|     return true; | ||||
| } | ||||
| inline bool convertInto( std::string const& _source, bool _dest ) { | ||||
|     std::string sourceLC = toLower( _source ); | ||||
|     if( sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) | ||||
|         _dest = true; | ||||
|     else if( sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) | ||||
|         _dest = false; | ||||
|     else | ||||
|         return false; | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| template<typename T> | ||||
| class Opt { | ||||
| public: | ||||
|     Opt( std::string const& _synposis ) : m_synopsis( _synposis ) {} | ||||
|     Opt& shortOpt( std::string const& _value ) { m_shortOpt = _value; return *this; } | ||||
|     Opt& longOpt( std::string const& _value )  { m_longOpt = _value; return *this; } | ||||
|     Opt& shortName( std::string const& _value ) { m_shortName = _value; return *this; } | ||||
|     Opt& longName( std::string const& _value )  { m_longName = _value; return *this; } | ||||
|  | ||||
|     template<typename M> | ||||
|     Opt& addArg( std::string const& _name, M const& _member  ){ | ||||
|         m_args.push_back( Arg( _name, _member ) ); | ||||
|     Opt& optArg( std::string const& _name, M const& _member  ){ | ||||
|         m_argName = _name; | ||||
|         m_field = new Field<M>( _member ); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     template<typename M> | ||||
|     Opt& flag( M const& _member  ){ | ||||
|         m_field = new FlagField<M>( _member ); | ||||
|         return *this; | ||||
|     } | ||||
|      | ||||
|     std::size_t takesArg() const { return !m_args.empty(); } | ||||
|  | ||||
|     std::size_t takesArg() const { return !m_argName.empty(); } | ||||
|     std::string synopsis() const { return m_synopsis; } | ||||
|     std::string shortOpt() const { return m_shortOpt; } | ||||
|     std::string longOpt() const { return m_longOpt; } | ||||
|     std::string shortName() const { return m_shortName; } | ||||
|     std::string longName() const { return m_longName; } | ||||
|      | ||||
|     bool parseInto( std::string const& _arg, T& _config ) const { | ||||
|         ensureWeightedArgsAreSorted(); | ||||
|         typename std::vector<Arg const*>::const_iterator | ||||
|             it = m_argsInWeightedOrder.begin(), | ||||
|             itEnd = m_argsInWeightedOrder.end(); | ||||
|         for( ; it != itEnd; ++it ) | ||||
|             if( (*it)->isMatch( _arg ) ) { | ||||
|                 (*it)->parseInto( _arg, _config ); | ||||
|                 return true; | ||||
|             } | ||||
|         return false; | ||||
|         if( m_argName.empty() ) | ||||
|             m_field->set( _config ); | ||||
|         else if( !m_field->parseInto( _arg, _config ) ) | ||||
|             throw std::domain_error( "'" + _arg + "' was not valid for <" + m_argName + ">" );                 | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     std::string usage() const { | ||||
|         std::ostringstream oss; | ||||
|         oss << *this; | ||||
|         writeToStream( oss ); | ||||
|         return oss.str(); | ||||
|     } | ||||
|     friend std::ostream& operator << ( std::ostream& os, Opt const& _opt ) { | ||||
|         if( !_opt.m_shortOpt.empty() ) | ||||
|             os << "-" << _opt.m_shortOpt; | ||||
|             if( !_opt.m_longOpt.empty() ) | ||||
|                 os << ", "; | ||||
|         if( !_opt.m_longOpt.empty() ) | ||||
|             os << "--" << _opt.m_longOpt; | ||||
|         if( !_opt.m_args.empty() ) { | ||||
|             os << " "; | ||||
|             typename std::vector<Arg>::const_iterator | ||||
|                 it = _opt.m_args.begin(), | ||||
|                 itEnd = _opt.m_args.end(); | ||||
|             while( it != itEnd ) { | ||||
|                 os << static_cast<ArgData const&>( *it ); | ||||
|                 if( ++it!=itEnd ) | ||||
|                     os << "|"; | ||||
|             } | ||||
|         } | ||||
|         _opt.writeToStream( os ); | ||||
|         return os; | ||||
|     } | ||||
|      | ||||
|     void writeToStream ( std::ostream& os ) const { | ||||
|         if( !m_shortName.empty() ) | ||||
|             os << "-" << m_shortName; | ||||
|             if( !m_longName.empty() ) | ||||
|                 os << ", "; | ||||
|         if( !m_longName.empty() ) | ||||
|             os << "--" << m_longName; | ||||
|         if( takesArg() ) | ||||
|             os << " <" << m_argName << ">"; | ||||
|     }     | ||||
| private: | ||||
|  | ||||
|     struct IField : SharedImpl<> { | ||||
|         virtual ~IField() {} | ||||
|         virtual bool parseInto( std::string const& _arg, T& _config ) const = 0; | ||||
|         virtual bool parseInto( std::string const&, T& ) const { | ||||
|             throw std::logic_error( "cannot pass argument to bool binder" ); | ||||
|         } | ||||
|         virtual void set( T& ) const { | ||||
|             throw std::logic_error( "field requires an argument" ); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|          | ||||
|     template<typename F> | ||||
|     struct Field; | ||||
|  | ||||
|     template<typename F> | ||||
|     struct FlagField; | ||||
|  | ||||
|     // Data member : with argument | ||||
|     template<typename C, typename M> | ||||
|     struct Field<M C::*> : IField { | ||||
|         Field( M C::* _member ) : member( _member ) {} | ||||
| @@ -147,11 +122,22 @@ private: | ||||
|         } | ||||
|         M C::* member; | ||||
|     }; | ||||
|     // Data member : flag | ||||
|     template<typename C, typename M> | ||||
|     struct FlagField<M C::*> : IField { | ||||
|         FlagField( M C::* _member ) : member( _member ) {} | ||||
|         void set( T& _config ) const { | ||||
|             _config.*member = true; | ||||
|         } | ||||
|         M C::* member; | ||||
|     }; | ||||
|      | ||||
|     // Unary method : with argument | ||||
|     template<typename C, typename M> | ||||
|     struct Field<void (C::*)( M )> : IField { | ||||
|         Field( void (C::*_method)( M ) ) : method( _method ) {} | ||||
|         bool parseInto( std::string const& _arg, T& _config ) const { | ||||
|             M value; | ||||
|             typename RemoveConstRef<M>::type value; | ||||
|             if( !convertInto( _arg, value ) ) | ||||
|                 return false; | ||||
|             ( _config.*method )( value ); | ||||
| @@ -159,42 +145,32 @@ private: | ||||
|         } | ||||
|         void (C::*method)( M ); | ||||
|     }; | ||||
|      | ||||
|     class Arg : public ArgData { | ||||
|     public: | ||||
|         Arg() : m_field( NULL ) {} | ||||
|         template<typename M> | ||||
|         Arg( std::string const& _name, M const& _member ) | ||||
|         :   ArgData( _name ), | ||||
|             m_field( new Field<M>( _member ) ) | ||||
|         {} | ||||
|         void parseInto( std::string const& _arg, T& _config ) const { | ||||
|             if( !m_field->parseInto( strip( _arg ), _config ) ) | ||||
|                 throw std::domain_error( "'" + _arg + "' was not valid for <" + m_name + ">" ); | ||||
|  | ||||
|     // Unary method : flag | ||||
|     template<typename C, typename M> | ||||
|     struct FlagField<void (C::*)( M )> : IField { | ||||
|         FlagField( void (C::*_method)( M ) ) : method( _method ) {} | ||||
|         void set( T& _config ) const { | ||||
|             ( _config.*method )( true ); | ||||
|         } | ||||
|                  | ||||
|     private: | ||||
|         Ptr<IField> m_field; | ||||
|         void (C::*method)( M ); | ||||
|     }; | ||||
|  | ||||
|     // Nullary method : flag | ||||
|     template<typename C> | ||||
|     struct FlagField<void (C::*)()> : IField { | ||||
|         FlagField( void (C::*_method)() ) : method( _method ) {} | ||||
|         void set( T& _config ) const { | ||||
|             ( _config.*method )(); | ||||
|         } | ||||
|         void (C::*method)(); | ||||
|     }; | ||||
|      | ||||
|     static bool argLess( Arg const* lhs, Arg const* rhs ) { | ||||
|         return *lhs < *rhs; | ||||
|     } | ||||
|     void ensureWeightedArgsAreSorted() const { | ||||
|         if( m_args.size() > m_argsInWeightedOrder.size() ) { | ||||
|             m_argsInWeightedOrder.clear(); | ||||
|             typename std::vector<Arg>::const_iterator   it = m_args.begin(), | ||||
|                                                         itEnd = m_args.end(); | ||||
|             for( ; it != itEnd; ++it ) | ||||
|                 m_argsInWeightedOrder.push_back( &*it ); | ||||
|             sort( m_argsInWeightedOrder.begin(), m_argsInWeightedOrder.end(), &Opt::argLess ); | ||||
|         } | ||||
|     } | ||||
|     std::string m_synopsis; | ||||
|     std::string m_shortOpt; | ||||
|     std::string m_longOpt; | ||||
|     std::vector<Arg> m_args; | ||||
|     mutable std::vector<Arg const*> m_argsInWeightedOrder; | ||||
|     std::string m_shortName; | ||||
|     std::string m_longName; | ||||
|     std::string m_argName; | ||||
|     Ptr<IField> m_field; | ||||
| }; | ||||
|  | ||||
| template<typename T> | ||||
| @@ -205,27 +181,34 @@ public: | ||||
|     :   m_separatorChars( "=: " ), | ||||
|         m_allowSpaceSeparator( m_separatorChars.find( ' ' ) != std::string::npos ) | ||||
|     {} | ||||
|     template<typename M> | ||||
|     Parser( std::string const&, M ) // !TBD | ||||
|     :   m_separatorChars( "=: " ), | ||||
|         m_allowSpaceSeparator( m_separatorChars.find( ' ' ) != std::string::npos ) | ||||
|     {} | ||||
|     template<typename M> | ||||
|     Parser& operator()( std::string const&, M ) { return *this; } // !TBD | ||||
|  | ||||
|     Opt<T>& addOption( std::string const& _synposis ) { | ||||
|         m_allOptionParsers.push_back( _synposis ); | ||||
|         return m_allOptionParsers.back(); | ||||
|     } | ||||
|  | ||||
|     void parseArgs( int argc, const char* const argv[], T& _config ) { | ||||
|     void parseInto( int argc, const char* const argv[], T& _config ) { | ||||
|         std::vector<std::string> args; | ||||
|         args.reserve( static_cast<std::size_t>( argc ) ); | ||||
|         for( int i = 0; i < argc; ++i ) | ||||
|             args.push_back( argv[i] ); | ||||
|          | ||||
|         parseArgs( args, _config ); | ||||
|         parseInto( args, _config ); | ||||
|     } | ||||
|  | ||||
|     template<typename U> | ||||
|     void parseRemainingArgs( Parser<U>& _parser, T& _config ) { | ||||
|         parseArgs( _parser.m_unusedOpts, _config ); | ||||
|         parseInto( _parser.m_unusedOpts, _config ); | ||||
|     } | ||||
|  | ||||
|     void parseArgs( std::vector<std::string> const& _args, T& _config ) { | ||||
|     void parseInto( std::vector<std::string> const& _args, T& _config ) { | ||||
|         ensureOptions(); | ||||
|         for( std::size_t i = 0; i < _args.size(); ++i ) { | ||||
|             std::string const& arg = _args[i]; | ||||
| @@ -297,10 +280,10 @@ private: | ||||
|             m_optionsByName.clear(); | ||||
|             typename std::vector<Opt<T> >::const_iterator it, itEnd = m_allOptionParsers.end(); | ||||
|             for( it = m_allOptionParsers.begin(); it != itEnd; ++it ) { | ||||
|                 if( !it->shortOpt().empty() ) | ||||
|                     m_optionsByName.insert( std::make_pair( "-" + it->shortOpt(), &*it ) ); | ||||
|                 if( !it->longOpt().empty() ) | ||||
|                     m_optionsByName.insert( std::make_pair( "--" + it->longOpt(), &*it ) );                 | ||||
|                 if( !it->shortName().empty() ) | ||||
|                     m_optionsByName.insert( std::make_pair( "-" + it->shortName(), &*it ) ); | ||||
|                 if( !it->longName().empty() ) | ||||
|                     m_optionsByName.insert( std::make_pair( "--" + it->longName(), &*it ) );                 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -319,12 +302,12 @@ private: | ||||
| } // end namespace Catch | ||||
|  | ||||
| struct TestOpt { | ||||
|     TestOpt() : number( 0 ), index( 0 ) {} | ||||
|     TestOpt() : number( 0 ), index( 0 ), flag( false ) {} | ||||
|  | ||||
|     std::string fileName; | ||||
|     std::string streamName; | ||||
|     int number; | ||||
|     int index; | ||||
|     bool flag; | ||||
|      | ||||
|     void setValidIndex( int i ) { | ||||
|         if( i < 0 || i > 10 ) | ||||
| @@ -337,120 +320,54 @@ struct TestOpt2 { | ||||
|     std::string description; | ||||
| }; | ||||
|  | ||||
| TEST_CASE( "Arg" ) { | ||||
|     SECTION( "pre and post" ) { | ||||
|         Clara::ArgData preAndPost( "prefix<arg>postfix" ); | ||||
|         CHECK( preAndPost.prefix() == "prefix" ); | ||||
|         CHECK( preAndPost.postfix() == "postfix" ); | ||||
|         CHECK( preAndPost.name() == "arg" ); | ||||
|          | ||||
|         CHECK( preAndPost.isMatch( "prefixpayloadpostfix" ) ); | ||||
|         CHECK( preAndPost.strip( "prefixpayloadpostfix" ) == "payload" ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "payload" ) ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "postfixpayloadpostfix" ) ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "prefixpayloadpostfixx" ) ); | ||||
|     } | ||||
|     SECTION( "pre" ) { | ||||
|         Clara::ArgData preAndPost( "prefix<arg>" ); | ||||
|         CHECK( preAndPost.prefix() == "prefix" ); | ||||
|         CHECK( preAndPost.postfix() == "" ); | ||||
|         CHECK( preAndPost.name() == "arg" ); | ||||
|          | ||||
|         CHECK( preAndPost.isMatch( "prefixpayload" ) ); | ||||
|         CHECK( preAndPost.strip( "prefixpayload" ) == "payload" ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "payload" ) ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "postfixpayload" ) ); | ||||
|     } | ||||
|     SECTION( "post" ) { | ||||
|         Clara::ArgData preAndPost( "<arg>postfix" ); | ||||
|         CHECK( preAndPost.prefix() == "" ); | ||||
|         CHECK( preAndPost.postfix() == "postfix" ); | ||||
|         CHECK( preAndPost.name() == "arg" ); | ||||
|  | ||||
|         CHECK( preAndPost.isMatch( "payloadpostfix" ) ); | ||||
|         CHECK( preAndPost.strip( "payloadpostfix" ) == "payload" ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "payload" ) ); | ||||
|         CHECK_FALSE( preAndPost.isMatch( "payloadpostfixx" ) ); | ||||
|     } | ||||
|     SECTION( "none" ) { | ||||
|         Clara::ArgData preAndPost( "<arg>" ); | ||||
|         CHECK( preAndPost.prefix() == "" ); | ||||
|         CHECK( preAndPost.postfix() == "" ); | ||||
|         CHECK( preAndPost.name() == "arg" ); | ||||
|  | ||||
|         CHECK( preAndPost.isMatch( "payload" ) ); | ||||
|         CHECK( preAndPost.strip( "payload" ) == "payload" ); | ||||
|     } | ||||
|     SECTION( "errors" ) { | ||||
|         CHECK_THROWS( Clara::ArgData( "" ) ); | ||||
|         CHECK_THROWS( Clara::ArgData( "no brackets" ) ); | ||||
|         CHECK_THROWS( Clara::ArgData( "<one bracket" ) ); | ||||
|         CHECK_THROWS( Clara::ArgData( "one bracket>" ) ); | ||||
|         CHECK_THROWS( Clara::ArgData( "><" ) ); | ||||
|         CHECK_THROWS( Clara::ArgData( "<>" ) ); | ||||
|     } | ||||
| } | ||||
|  | ||||
| TEST_CASE( "cmdline", "" ) { | ||||
|  | ||||
|     TestOpt config; | ||||
|     Clara::Parser<TestOpt> parser; | ||||
|     parser.addOption( "specifies output file" ) | ||||
|         .shortOpt( "o" ) | ||||
|         .longOpt( "output" ) | ||||
|         .addArg( "<filename>", &TestOpt::fileName ) | ||||
|         .addArg( "%<stream name>", &TestOpt::streamName ); | ||||
|         .shortName( "o" ) | ||||
|         .longName( "output" ) | ||||
|         .optArg( "<filename>", &TestOpt::fileName ); | ||||
|  | ||||
|     SECTION( "plain filename" ) { | ||||
|         const char* argv[] = { "test", "-o filename.ext" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.fileName == "filename.ext" ); | ||||
|         CHECK( config.streamName == "" ); | ||||
|     } | ||||
|     SECTION( "plain filename with colon" ) { | ||||
|         const char* argv[] = { "test", "-o:filename.ext" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.fileName == "filename.ext" ); | ||||
|         CHECK( config.streamName == "" ); | ||||
|     } | ||||
|     SECTION( "plain filename with =" ) { | ||||
|         const char* argv[] = { "test", "-o=filename.ext" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.fileName == "filename.ext" ); | ||||
|         CHECK( config.streamName == "" ); | ||||
|     } | ||||
|     SECTION( "stream name" ) { | ||||
|         const char* argv[] = { "test", "-o %stdout" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.fileName == "" ); | ||||
|         CHECK( config.streamName == "stdout" ); | ||||
|     } | ||||
|     SECTION( "long opt" ) { | ||||
|         const char* argv[] = { "test", "--output %stdout" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.fileName == "" ); | ||||
|         CHECK( config.streamName == "stdout" ); | ||||
|         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.fileName == "%stdout" ); | ||||
|     } | ||||
|      | ||||
|     parser.addOption( "a number" ) | ||||
|             .shortOpt( "n" ) | ||||
|             .addArg( "<an integral value>", &TestOpt::number ); | ||||
|             .shortName( "n" ) | ||||
|             .optArg( "<an integral value>", &TestOpt::number ); | ||||
|      | ||||
|     SECTION( "a number" ) { | ||||
|         const char* argv[] = { "test", "-n 42" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|         CHECK( config.number == 42 ); | ||||
|     } | ||||
|     SECTION( "not a number" ) { | ||||
|         const char* argv[] = { "test", "-n forty-two" }; | ||||
|  | ||||
|         CHECK_THROWS( parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ) ); | ||||
|         CHECK_THROWS( parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ) ); | ||||
|         CHECK( config.number == 0 ); | ||||
|     } | ||||
|      | ||||
| @@ -461,13 +378,13 @@ TEST_CASE( "cmdline", "" ) { | ||||
|         Clara::Parser<TestOpt2> parser2; | ||||
|  | ||||
|         parser2.addOption( "description" ) | ||||
|                     .shortOpt( "d" ) | ||||
|                     .longOpt( "description" ) | ||||
|                     .addArg( "<some text>", &TestOpt2::description ); | ||||
|                     .shortName( "d" ) | ||||
|                     .longName( "description" ) | ||||
|                     .optArg( "<some text>", &TestOpt2::description ); | ||||
|          | ||||
|         const char* argv[] = { "test", "-n 42", "-d some text" }; | ||||
|  | ||||
|         parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config1 ); | ||||
|         parser.parseInto( sizeof(argv)/sizeof(char*), argv, config1 ); | ||||
|         CHECK( config1.number == 42 ); | ||||
|          | ||||
|         parser2.parseRemainingArgs( parser, config2 ); | ||||
| @@ -477,22 +394,134 @@ TEST_CASE( "cmdline", "" ) { | ||||
|  | ||||
|     SECTION( "methods" ) { | ||||
|         parser.addOption( "An index, which is an integer between 0 and 10, inclusive" ) | ||||
|                 .shortOpt( "i" ) | ||||
|                 .addArg( "<index>", &TestOpt::setValidIndex ); | ||||
|                 .shortName( "i" ) | ||||
|                 .optArg( "<index>", &TestOpt::setValidIndex ); | ||||
|  | ||||
|         SECTION( "in range" ) { | ||||
|             const char* argv[] = { "test", "-i 3" }; | ||||
|  | ||||
|             parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|             parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|             REQUIRE( config.index == 3 ); | ||||
|         } | ||||
|         SECTION( "out of range" ) { | ||||
|             const char* argv[] = { "test", "-i 42" }; | ||||
|  | ||||
|             REQUIRE_THROWS( parser.parseArgs( sizeof(argv)/sizeof(char*), argv, config ) ); | ||||
|             REQUIRE_THROWS( parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ) ); | ||||
|         } | ||||
|          | ||||
|         std::cout << parser << std::endl; | ||||
|     } | ||||
|      | ||||
|     SECTION( "flags" ) { | ||||
|         parser.addOption( "A flag" ) | ||||
|                 .shortName( "f" ) | ||||
|                 .flag( &TestOpt::flag ); | ||||
|          | ||||
|         SECTION( "set" ) { | ||||
|             const char* argv[] = { "test", "-f" }; | ||||
|  | ||||
|             parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|             REQUIRE( config.flag ); | ||||
|         } | ||||
|         SECTION( "not set" ) { | ||||
|             const char* argv[] = { "test" }; | ||||
|  | ||||
|             parser.parseInto( sizeof(argv)/sizeof(char*), argv, config ); | ||||
|             REQUIRE( config.flag == false ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct Config { | ||||
|     bool listTests; | ||||
|     bool listTags; | ||||
|     bool showPassingTests; | ||||
|     bool breakIntoDebugger; | ||||
|     bool noThrow; | ||||
|     bool showHelp; | ||||
|      | ||||
|     int abortAfter; | ||||
|     std::string reporterName; | ||||
|     std::string fileName; | ||||
|     std::string suiteName; | ||||
|     std::vector<std::string> warnings; | ||||
|      | ||||
|     void abortAfterFirst() { abortAfter = 1; } | ||||
|     void abortAfterX( int x ) { abortAfter = x; } | ||||
|     void addWarning( std::string const& _warning ) { warnings.push_back( _warning ); } | ||||
| }; | ||||
|  | ||||
|  | ||||
| TEST_CASE( "growing new Catch cli" ) { | ||||
|     Clara::Parser<Config> parser; | ||||
|  | ||||
|     Clara::Parser<Config> | ||||
|         ( "-h, --help                         display usage information",           &Config::showHelp ) | ||||
|         ( "-l, --list                         list all (or matching) test cases",   &Config::listTests ) | ||||
|         ( "-t, --tags                         list all (or matching) tags",         &Config::listTags ) | ||||
|         ( "-p, --passing                      show passing test output",            &Config::showPassingTests ) | ||||
|         ( "-b, --break                        break into debugger on failure",      &Config::breakIntoDebugger ) | ||||
|         ( "-e, --nothrow                      Skip exception tests",                &Config::noThrow ) | ||||
|         ( "-o, --out <file name>              output filename",                     &Config::fileName ) | ||||
|         ( "-n, --name <name>                  suite name",                          &Config::suiteName ) | ||||
|         ( "-a, --abort                        abort at first failure",              &Config::abortAfterFirst ) | ||||
|         ( "-x, --abortx <number of failures>  abort after x failures",              &Config::abortAfterX ) | ||||
|         ( "-w, --warn <warning name>          enables warnings",                    &Config::addWarning ); | ||||
| //        .parseInto( argc, argv, config ); | ||||
|  | ||||
|     parser.addOption( "display usage information" ) | ||||
|         .shortName( "?") | ||||
|         .shortName( "h") | ||||
|         .longName( "help" ) | ||||
|         .flag( &Config::showHelp ); | ||||
|  | ||||
|     parser.addOption( "list all (or matching) test cases" ) | ||||
|         .shortName( "l") | ||||
|         .longName( "list" ) | ||||
|         .flag( &Config::listTests ); | ||||
|  | ||||
|     parser.addOption( "list all (or matching) tags" ) | ||||
|         .shortName( "t") | ||||
|         .longName( "tags" ) | ||||
|         .flag( &Config::listTags ); | ||||
|  | ||||
|     parser.addOption( "show passing test output" ) | ||||
|         .shortName( "p") | ||||
|         .longName( "passing" ) | ||||
|         .flag( &Config::showPassingTests ); | ||||
|  | ||||
|     parser.addOption( "break into debugger on failure" ) | ||||
|         .shortName( "b") | ||||
|         .longName( "break" ) | ||||
|         .flag( &Config::breakIntoDebugger ); | ||||
|  | ||||
|     parser.addOption( "Skip exception tests" ) | ||||
|         .shortName( "e") | ||||
|         .longName( "nothrow" ) | ||||
|         .flag( &Config::noThrow ); | ||||
|  | ||||
|     parser.addOption( "output filename" ) | ||||
|         .shortName( "o") | ||||
|         .longName( "out" ) | ||||
|         .optArg( "<file name>", &Config::fileName ); | ||||
|  | ||||
|     parser.addOption( "suite name" ) | ||||
|         .shortName( "n") | ||||
|         .longName( "name" ) | ||||
|         .optArg( "<name>", &Config::suiteName ); | ||||
|  | ||||
|     parser.addOption( "abort at first failure" ) | ||||
|         .shortName( "a") | ||||
|         .longName( "abort" ) | ||||
|         .flag( &Config::abortAfterFirst ); | ||||
|  | ||||
|     parser.addOption( "abort after x failures" ) | ||||
|         .shortName( "x") | ||||
|         .longName( "abortx" ) | ||||
|         .optArg( "<number of failures>", &Config::abortAfterX ); | ||||
|  | ||||
|     parser.addOption( "enables warnings" ) | ||||
|         .shortName( "w") | ||||
|         .longName( "warn" ) | ||||
|         .optArg( "<warning name>", &Config::addWarning ); | ||||
|  | ||||
|     std::cout << parser << std::endl; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Phil Nash
					Phil Nash