1 /* 2 * Created by Phil on 15/5/2013. 3 * Copyright 2014 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 #ifndef TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED 9 #define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED 10 11 #ifdef __clang__ 12 #pragma clang diagnostic push 13 #pragma clang diagnostic ignored "-Wpadded" 14 #endif 15 16 #include "catch_test_spec.hpp" 17 #include "catch_interfaces_tag_alias_registry.h" 18 19 namespace Catch { 20 21 class TestSpecParser { 22 enum Mode{ None, Name, QuotedName, Tag, EscapedName }; 23 Mode m_mode; 24 bool m_exclusion; 25 std::size_t m_start, m_pos; 26 std::string m_arg; 27 std::vector<std::size_t> m_escapeChars; 28 TestSpec::Filter m_currentFilter; 29 TestSpec m_testSpec; 30 ITagAliasRegistry const* m_tagAliases; 31 32 public: TestSpecParser(ITagAliasRegistry const & tagAliases)33 TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {} 34 parse(std::string const & arg)35 TestSpecParser& parse( std::string const& arg ) { 36 m_mode = None; 37 m_exclusion = false; 38 m_start = std::string::npos; 39 m_arg = m_tagAliases->expandAliases( arg ); 40 m_escapeChars.clear(); 41 for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) 42 visitChar( m_arg[m_pos] ); 43 if( m_mode == Name ) 44 addPattern<TestSpec::NamePattern>(); 45 return *this; 46 } testSpec()47 TestSpec testSpec() { 48 addFilter(); 49 return m_testSpec; 50 } 51 private: visitChar(char c)52 void visitChar( char c ) { 53 if( m_mode == None ) { 54 switch( c ) { 55 case ' ': return; 56 case '~': m_exclusion = true; return; 57 case '[': return startNewMode( Tag, ++m_pos ); 58 case '"': return startNewMode( QuotedName, ++m_pos ); 59 case '\\': return escape(); 60 default: startNewMode( Name, m_pos ); break; 61 } 62 } 63 if( m_mode == Name ) { 64 if( c == ',' ) { 65 addPattern<TestSpec::NamePattern>(); 66 addFilter(); 67 } 68 else if( c == '[' ) { 69 if( subString() == "exclude:" ) 70 m_exclusion = true; 71 else 72 addPattern<TestSpec::NamePattern>(); 73 startNewMode( Tag, ++m_pos ); 74 } 75 else if( c == '\\' ) 76 escape(); 77 } 78 else if( m_mode == EscapedName ) 79 m_mode = Name; 80 else if( m_mode == QuotedName && c == '"' ) 81 addPattern<TestSpec::NamePattern>(); 82 else if( m_mode == Tag && c == ']' ) 83 addPattern<TestSpec::TagPattern>(); 84 } startNewMode(Mode mode,std::size_t start)85 void startNewMode( Mode mode, std::size_t start ) { 86 m_mode = mode; 87 m_start = start; 88 } escape()89 void escape() { 90 if( m_mode == None ) 91 m_start = m_pos; 92 m_mode = EscapedName; 93 m_escapeChars.push_back( m_pos ); 94 } subString() const95 std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } 96 template<typename T> addPattern()97 void addPattern() { 98 std::string token = subString(); 99 for( size_t i = 0; i < m_escapeChars.size(); ++i ) 100 token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); 101 m_escapeChars.clear(); 102 if( startsWith( token, "exclude:" ) ) { 103 m_exclusion = true; 104 token = token.substr( 8 ); 105 } 106 if( !token.empty() ) { 107 Ptr<TestSpec::Pattern> pattern = new T( token ); 108 if( m_exclusion ) 109 pattern = new TestSpec::ExcludedPattern( pattern ); 110 m_currentFilter.m_patterns.push_back( pattern ); 111 } 112 m_exclusion = false; 113 m_mode = None; 114 } addFilter()115 void addFilter() { 116 if( !m_currentFilter.m_patterns.empty() ) { 117 m_testSpec.m_filters.push_back( m_currentFilter ); 118 m_currentFilter = TestSpec::Filter(); 119 } 120 } 121 }; parseTestSpec(std::string const & arg)122 inline TestSpec parseTestSpec( std::string const& arg ) { 123 return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); 124 } 125 126 } // namespace Catch 127 128 #ifdef __clang__ 129 #pragma clang diagnostic pop 130 #endif 131 132 #endif // TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED 133