mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	Refactored command line parsers into polymorphic classes
This commit is contained in:
		| @@ -174,6 +174,8 @@ namespace Catch { | ||||
|         try { | ||||
|             CommandParser parser( argc, argv ); | ||||
|  | ||||
|             AllOptions options; | ||||
|              | ||||
|             if( Command cmd = parser.find( "-h", "-?", "--help" ) ) { | ||||
|                 if( cmd.argsCount() != 0 ) | ||||
|                     cmd.raiseError( "Does not accept arguments" ); | ||||
| @@ -183,7 +185,7 @@ namespace Catch { | ||||
|                 return 0; | ||||
|             } | ||||
|          | ||||
|             parseIntoConfig( parser, config.data() );             | ||||
|             options.parseIntoConfig( parser, config.data() ); | ||||
|         } | ||||
|         catch( std::exception& ex ) { | ||||
|             std::cerr << ex.what() << "\n\nUsage: ...\n\n"; | ||||
|   | ||||
| @@ -72,6 +72,9 @@ namespace Catch { | ||||
|             return find( shortArg ) + find( longArg ); | ||||
|         } | ||||
|         Command find( const std::string& arg ) const { | ||||
|             if( arg.empty() ) | ||||
|                 return getArgs( "", 1 ); | ||||
|             else | ||||
|                 for( std::size_t i = 1; i < m_argc; ++i  ) | ||||
|                     if( m_argv[i] == arg ) | ||||
|                         return getArgs( m_argv[i], i+1 ); | ||||
| @@ -93,9 +96,81 @@ namespace Catch { | ||||
|         char const * const * m_argv; | ||||
|     }; | ||||
|  | ||||
|     inline void parseIntoConfig( const CommandParser& parser, ConfigData& config ) { | ||||
|     class OptionParser : public IShared { | ||||
|     public: | ||||
|         virtual ~OptionParser() {} | ||||
|  | ||||
|         if( Command cmd = parser.find( "-l", "--list" ) ) { | ||||
|         void parseIntoConfig( CommandParser parser, ConfigData& config ) { | ||||
|             Command cmd; | ||||
|             for( std::vector<std::string>::const_iterator it = m_optionNames.begin(); | ||||
|                 it != m_optionNames.end(); | ||||
|                 ++it ) | ||||
|                 cmd += parser.find( *it ); | ||||
|  | ||||
|             if( cmd ) | ||||
|                 parseIntoConfig( cmd, config ); | ||||
|         } | ||||
|  | ||||
|         virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) = 0; | ||||
|         virtual std::string argsSynopsis() const = 0; | ||||
|         virtual std::string optionSummary() const = 0; | ||||
|          | ||||
|     protected: | ||||
|         std::vector<std::string> m_optionNames; | ||||
|     }; | ||||
|  | ||||
|     namespace Options { | ||||
|  | ||||
|         class TestCaseOptionParser : public OptionParser { | ||||
|         public: | ||||
|             TestCaseOptionParser() { | ||||
|                 m_optionNames.push_back( "-t" ); | ||||
|                 m_optionNames.push_back( "--test" ); | ||||
|                 m_optionNames.push_back( "" ); // default option | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "<testspec> [<testspec>...]"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "Specify which test case or cases to run"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() == 0 ) | ||||
|                     cmd.raiseError( "Expected at least one argument" ); | ||||
|                 std::string groupName; | ||||
|                 for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { | ||||
|                     if( i != 0 ) | ||||
|                         groupName += " "; | ||||
|                     groupName += cmd[i]; | ||||
|                 } | ||||
|                 TestCaseFilters filters( groupName ); | ||||
|                 for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { | ||||
|                     if( startsWith( cmd[i], "exclude:" ) ) | ||||
|                         filters.addFilter( TestCaseFilter( cmd[i].substr( 8 ), IfFilterMatches::ExcludeTests ) ); | ||||
|                     else if( startsWith( cmd[i], "~" ) ) | ||||
|                         filters.addFilter( TestCaseFilter( cmd[i].substr( 1 ), IfFilterMatches::ExcludeTests ) ); | ||||
|                     else | ||||
|                         filters.addFilter( TestCaseFilter( cmd[i] ) ); | ||||
|                 } | ||||
|                 config.filters.push_back( filters ); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         class ListOptionParser : public OptionParser { | ||||
|         public: | ||||
|             ListOptionParser() { | ||||
|                 m_optionNames.push_back( "-l" ); | ||||
|                 m_optionNames.push_back( "--list" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() > 2 ) | ||||
|                     cmd.raiseError( "Expected upto 2 arguments" ); | ||||
|  | ||||
| @@ -119,35 +194,42 @@ namespace Catch { | ||||
|                         cmd.raiseError( "Expected [xml] or [text]" ); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         if( Command cmd = parser.find( "-t", "--test" ) + parser.getDefaultArgs() ) { | ||||
|             if( cmd.argsCount() == 0 ) | ||||
|                 cmd.raiseError( "Expected at least one argument" ); | ||||
|             std::string groupName; | ||||
|             for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { | ||||
|                 if( i != 0 ) | ||||
|                     groupName += " "; | ||||
|                 groupName += cmd[i]; | ||||
|         class ReporterOptionParser : public OptionParser { | ||||
|         public: | ||||
|             ReporterOptionParser() { | ||||
|                 m_optionNames.push_back( "-r" ); | ||||
|                 m_optionNames.push_back( "--reporter" ); | ||||
|             } | ||||
|             TestCaseFilters filters( groupName ); | ||||
|             for( std::size_t i = 0; i < cmd.argsCount(); ++i ) { | ||||
|                 if( startsWith( cmd[i], "exclude:" ) ) | ||||
|                     filters.addFilter( TestCaseFilter( cmd[i].substr( 8 ), IfFilterMatches::ExcludeTests ) ); | ||||
|                 else if( startsWith( cmd[i], "~" ) ) | ||||
|                     filters.addFilter( TestCaseFilter( cmd[i].substr( 1 ), IfFilterMatches::ExcludeTests ) ); | ||||
|                 else | ||||
|                     filters.addFilter( TestCaseFilter( cmd[i] ) ); | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             config.filters.push_back( filters ); | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|         if( Command cmd = parser.find( "-r", "--reporter" ) ) { | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() != 1 ) | ||||
|                     cmd.raiseError( "Expected one argument" ); | ||||
|                 config.reporter = cmd[0]; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if( Command cmd = parser.find( "-o", "--out" ) ) { | ||||
|         class OutputOptionParser : public OptionParser { | ||||
|         public: | ||||
|             OutputOptionParser() { | ||||
|                 m_optionNames.push_back( "-o" ); | ||||
|                 m_optionNames.push_back( "--out" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() == 0 ) | ||||
|                     cmd.raiseError( "Expected filename" ); | ||||
|                 if( cmd[0][0] == '%' ) | ||||
| @@ -155,26 +237,82 @@ namespace Catch { | ||||
|                 else | ||||
|                     config.outputFilename = cmd[0]; | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         if( Command cmd = parser.find( "-s", "--success" ) ) { | ||||
|         class SuccesssOptionParser : public OptionParser { | ||||
|         public: | ||||
|             SuccesssOptionParser() { | ||||
|                 m_optionNames.push_back( "-s" ); | ||||
|                 m_optionNames.push_back( "--success" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() != 0 ) | ||||
|                     cmd.raiseError( "Does not accept arguments" ); | ||||
|                 config.includeWhichResults = Include::SuccessfulResults; | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         if( Command cmd = parser.find( "-b", "--break" ) ) { | ||||
|         class DebugBreakOptionParser : public OptionParser { | ||||
|         public: | ||||
|             DebugBreakOptionParser() { | ||||
|                 m_optionNames.push_back( "-b" ); | ||||
|                 m_optionNames.push_back( "--break" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() != 0 ) | ||||
|                     cmd.raiseError( "Does not accept arguments" ); | ||||
|                 config.shouldDebugBreak = true; | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         if( Command cmd = parser.find( "-n", "--name" ) ) { | ||||
|         class NameOptionParser : public OptionParser { | ||||
|         public: | ||||
|             NameOptionParser() { | ||||
|                 m_optionNames.push_back( "-n" ); | ||||
|                 m_optionNames.push_back( "--name" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() != 1 ) | ||||
|                     cmd.raiseError( "Expected a name" ); | ||||
|                 config.name = cmd[0]; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if( Command cmd = parser.find( "-a", "--abort" ) ) { | ||||
|         class AbortOptionParser : public OptionParser { | ||||
|         public: | ||||
|             AbortOptionParser() { | ||||
|                 m_optionNames.push_back( "-a" ); | ||||
|                 m_optionNames.push_back( "--abort" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() > 1 ) | ||||
|                     cmd.raiseError( "Only accepts 0-1 arguments" ); | ||||
|                 int threshold = 1; | ||||
| @@ -187,15 +325,69 @@ namespace Catch { | ||||
|                 } | ||||
|                 config.cutoff = threshold; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if( Command cmd = parser.find( "-nt", "--nothrow" ) ) { | ||||
|         class NoThrowOptionParser : public OptionParser { | ||||
|         public: | ||||
|             NoThrowOptionParser() { | ||||
|                 m_optionNames.push_back( "-nt" ); | ||||
|                 m_optionNames.push_back( "--nothrow" ); | ||||
|             } | ||||
|             virtual std::string argsSynopsis() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|             virtual std::string optionSummary() const { | ||||
|                 return "!TBD"; | ||||
|             } | ||||
|  | ||||
|             virtual void parseIntoConfig( const Command& cmd, ConfigData& config ) { | ||||
|                 if( cmd.argsCount() != 0 ) | ||||
|                     cmd.raiseError( "Does not accept arguments" ); | ||||
|                 config.allowThrows = false; | ||||
|             } | ||||
|  | ||||
|         }; | ||||
|     } | ||||
|      | ||||
|     class AllOptions | ||||
|     { | ||||
|     public: | ||||
|         typedef std::vector<Ptr<OptionParser> > Parsers; | ||||
|         typedef Parsers::const_iterator const_iterator; | ||||
|         typedef Parsers::const_iterator iterator; | ||||
|  | ||||
|         AllOptions() { | ||||
|             add<Options::AbortOptionParser>(); | ||||
|             add<Options::DebugBreakOptionParser>(); | ||||
|             add<Options::ListOptionParser>(); | ||||
|             add<Options::NameOptionParser>(); | ||||
|             add<Options::NoThrowOptionParser>(); | ||||
|             add<Options::OutputOptionParser>(); | ||||
|             add<Options::ReporterOptionParser>(); | ||||
|             add<Options::SuccesssOptionParser>(); | ||||
|             add<Options::TestCaseOptionParser>(); | ||||
|         } | ||||
|  | ||||
|         void parseIntoConfig( const CommandParser& parser, ConfigData& config ) { | ||||
|             for( const_iterator it = m_parsers.begin(); it != m_parsers.end(); ++it ) | ||||
|                 (*it)->parseIntoConfig( parser, config ); | ||||
|         } | ||||
|  | ||||
|         const_iterator begin() const { | ||||
|             return m_parsers.begin(); | ||||
|         } | ||||
|         const_iterator end() const { | ||||
|             return m_parsers.end(); | ||||
|         } | ||||
|     private: | ||||
|  | ||||
|         template<typename T> | ||||
|         void add() { | ||||
|             m_parsers.push_back( new SharedImpl<T>() ); | ||||
|         } | ||||
|         Parsers m_parsers; | ||||
|  | ||||
|     }; | ||||
|          | ||||
| } // end namespace Catch | ||||
|  | ||||
| #endif // TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED | ||||
|   | ||||
| @@ -70,13 +70,14 @@ TEST_CASE( "meta/Misc/Sections", "looped tests" ) { | ||||
|  | ||||
| template<size_t size> | ||||
| void parseIntoConfig( const char * (&argv)[size], Catch::ConfigData& config ) { | ||||
|     Catch::parseIntoConfig( Catch::CommandParser( size, argv ), config ); | ||||
|     static Catch::AllOptions options; | ||||
|     options.parseIntoConfig( Catch::CommandParser( size, argv ), config ); | ||||
| } | ||||
|  | ||||
| template<size_t size> | ||||
| std::string parseIntoConfigAndReturnError( const char * (&argv)[size], Catch::ConfigData& config ) { | ||||
|     try { | ||||
|         Catch::parseIntoConfig( Catch::CommandParser( size, argv ), config ); | ||||
|         parseIntoConfig( argv, config ); | ||||
|         FAIL( "expected exception" ); | ||||
|     } | ||||
|     catch( std::exception& ex ) { | ||||
| @@ -306,3 +307,30 @@ TEST_CASE( "selftest/filter/wildcard at both ends", "Individual filters with wil | ||||
|     CHECK( matchBadgers.shouldInclude( makeTestCase( "badgers are big" ) ) ); | ||||
|     CHECK( matchBadgers.shouldInclude( makeTestCase( "hedgehogs" ) ) == false ); | ||||
| } | ||||
|  | ||||
|  | ||||
| template<size_t size> | ||||
| int getArgc( const char * (&)[size] ) { | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| TEST_CASE( "selftest/option parsers", "" ) | ||||
| { | ||||
|     Catch::ConfigData config; | ||||
|      | ||||
|     Catch::SharedImpl<Catch::Options::TestCaseOptionParser> tcOpt; | ||||
|     Catch::OptionParser& opt = tcOpt; | ||||
|  | ||||
|     const char* argv[] = { "test", "-t", "test1" }; | ||||
|  | ||||
|     Catch::CommandParser parser( getArgc( argv ), argv ); | ||||
|  | ||||
|     CHECK_NOTHROW( opt.parseIntoConfig( parser, config ) ); | ||||
|  | ||||
|     REQUIRE( config.filters.size() == 1 ); | ||||
|     REQUIRE( config.filters[0].shouldInclude( makeTestCase( "notIncluded" ) ) == false ); | ||||
|     REQUIRE( config.filters[0].shouldInclude( makeTestCase( "test1" ) ) ); | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Phil Nash
					Phil Nash