/* * Created by Martin on 19/07/2017. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #include "catch_test_spec_parser.h" namespace Catch { TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} TestSpecParser& TestSpecParser::parse( std::string const& arg ) { m_mode = None; m_exclusion = false; m_arg = m_tagAliases->expandAliases( arg ); m_escapeChars.clear(); m_substring.reserve(m_arg.size()); m_patternName.reserve(m_arg.size()); for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) visitChar( m_arg[m_pos] ); endMode(); return *this; } TestSpec TestSpecParser::testSpec() { addFilter(); return m_testSpec; } void TestSpecParser::visitChar( char c ) { if( (m_mode != EscapedName) && (c == '\\') ) { escape(); m_substring += c; m_patternName += c; return; }else if((m_mode != EscapedName) && (c == ',') ) { endMode(); addFilter(); return; } switch( m_mode ) { case None: if( processNoneChar( c ) ) return; break; case Name: processNameChar( c ); break; case EscapedName: endMode(); break; default: case Tag: case QuotedName: if( processOtherChar( c ) ) return; break; } m_substring += c; if( !isControlChar( c ) ) m_patternName += c; } // Two of the processing methods return true to signal the caller to return // without adding the given character to the current pattern strings bool TestSpecParser::processNoneChar( char c ) { switch( c ) { case ' ': return true; case '~': m_exclusion = true; return false; case '[': startNewMode( Tag ); return false; case '"': startNewMode( QuotedName ); return false; default: startNewMode( Name ); return false; } } void TestSpecParser::processNameChar( char c ) { if( c == '[' ) { if( m_substring == "exclude:" ) m_exclusion = true; else endMode(); startNewMode( Tag ); } } bool TestSpecParser::processOtherChar( char c ) { if( !isControlChar( c ) ) return false; m_substring += c; endMode(); return true; } void TestSpecParser::startNewMode( Mode mode ) { m_mode = mode; } void TestSpecParser::endMode() { switch( m_mode ) { case Name: case QuotedName: return addPattern(); case Tag: return addPattern(); case EscapedName: revertBackToLastMode(); return; case None: default: return startNewMode( None ); } } void TestSpecParser::escape() { saveLastMode(); m_mode = EscapedName; m_escapeChars.push_back( m_pos ); } bool TestSpecParser::isControlChar( char c ) const { switch( m_mode ) { default: return false; case None: return c == '~'; case Name: return c == '['; case EscapedName: return true; case QuotedName: return c == '"'; case Tag: return c == '[' || c == ']'; } } void TestSpecParser::addFilter() { if( !m_currentFilter.m_patterns.empty() ) { m_testSpec.m_filters.push_back( m_currentFilter ); m_currentFilter = TestSpec::Filter(); } } void TestSpecParser::saveLastMode() { lastMode = m_mode; } void TestSpecParser::revertBackToLastMode() { m_mode = lastMode; } TestSpec parseTestSpec( std::string const& arg ) { return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } } // namespace Catch