Fixes #1766: Catch terminates when parsing invalid test name

This commit is contained in:
amitherman95 2019-10-19 16:50:46 +03:00 committed by Jozef Grajciar
parent 01ef7076f5
commit 84856844e1
12 changed files with 88 additions and 28 deletions

View File

@ -49,9 +49,15 @@ namespace Catch {
if( !line.empty() && !startsWith( line, '#' ) ) { if( !line.empty() && !startsWith( line, '#' ) ) {
if( !startsWith( line, '"' ) ) if( !startsWith( line, '"' ) )
line = '"' + line + '"'; line = '"' + line + '"';
config.testsOrTags.push_back( line + ',' ); config.testsOrTags.push_back( line );
config.testsOrTags.push_back( "," );
} }
} }
//Remove comma in the end
if(!config.testsOrTags.empty())
config.testsOrTags.erase( config.testsOrTags.end()-1 );
return ParserResult::ok( ParseResultType::Matched ); return ParserResult::ok( ParseResultType::Matched );
}; };
auto const setTestOrder = [&]( std::string const& order ) { auto const setTestOrder = [&]( std::string const& order ) {

View File

@ -214,6 +214,8 @@ namespace Catch {
virtual void noMatchingTestCases( std::string const& spec ) = 0; virtual void noMatchingTestCases( std::string const& spec ) = 0;
virtual void reportInvalidArguments(std::string const&) {}
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;

View File

@ -68,8 +68,9 @@ namespace Catch {
{ {
auto const& allTestCases = getAllTestCasesSorted(*m_config); auto const& allTestCases = getAllTestCasesSorted(*m_config);
m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
if (m_matches.empty()) { if (m_matches.empty() && invalidArgs.empty()) {
for (auto const& test : allTestCases) for (auto const& test : allTestCases)
if (!test.isHidden()) if (!test.isHidden())
m_tests.emplace(&test); m_tests.emplace(&test);
@ -80,6 +81,7 @@ namespace Catch {
} }
Totals execute() { Totals execute() {
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
Totals totals; Totals totals;
m_context.testGroupStarting(m_config->name(), 1, 1); m_context.testGroupStarting(m_config->name(), 1, 1);
for (auto const& testCase : m_tests) { for (auto const& testCase : m_tests) {
@ -95,6 +97,12 @@ namespace Catch {
totals.error = -1; totals.error = -1;
} }
} }
if (!invalidArgs.empty()) {
for (auto const& invalidArg: invalidArgs)
m_context.reporter().reportInvalidArguments(invalidArg);
}
m_context.testGroupEnded(m_config->name(), totals, 1, 1); m_context.testGroupEnded(m_config->name(), totals, 1, 1);
return totals; return totals;
} }

View File

@ -92,4 +92,8 @@ namespace Catch {
return matches; return matches;
} }
const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
return (m_invalidArgs);
}
} }

View File

@ -73,14 +73,16 @@ namespace Catch {
std::vector<TestCase const*> tests; std::vector<TestCase const*> tests;
}; };
using Matches = std::vector<FilterMatch>; using Matches = std::vector<FilterMatch>;
using vectorStrings = std::vector<std::string>;
bool hasFilters() const; bool hasFilters() const;
bool matches( TestCaseInfo const& testCase ) const; bool matches( TestCaseInfo const& testCase ) const;
Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const; Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
const vectorStrings & getInvalidArgs() const;
private: private:
std::vector<Filter> m_filters; std::vector<Filter> m_filters;
std::vector<std::string> m_invalidArgs;
friend class TestSpecParser; friend class TestSpecParser;
}; };
} }

View File

@ -20,8 +20,13 @@ namespace Catch {
m_substring.reserve(m_arg.size()); m_substring.reserve(m_arg.size());
m_patternName.reserve(m_arg.size()); m_patternName.reserve(m_arg.size());
m_realPatternPos = 0; m_realPatternPos = 0;
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
visitChar( m_arg[m_pos] ); //if visitChar fails
if( !visitChar( m_arg[m_pos] ) ){
m_testSpec.m_invalidArgs.push_back(arg);
break;
}
endMode(); endMode();
return *this; return *this;
} }
@ -29,38 +34,32 @@ namespace Catch {
addFilter(); addFilter();
return m_testSpec; return m_testSpec;
} }
void TestSpecParser::visitChar( char c ) { bool TestSpecParser::visitChar( char c ) {
if( (m_mode != EscapedName) && (c == '\\') ) { if( (m_mode != EscapedName) && (c == '\\') ) {
escape(); escape();
m_substring += c; addCharToPattern(c);
m_patternName += c; return true;
m_realPatternPos++;
return;
}else if((m_mode != EscapedName) && (c == ',') ) { }else if((m_mode != EscapedName) && (c == ',') ) {
endMode(); return separate();
addFilter();
return;
} }
switch( m_mode ) { switch( m_mode ) {
case None: case None:
if( processNoneChar( c ) ) if( processNoneChar( c ) )
return; return true;
break; break;
case Name: case Name:
processNameChar( c ); processNameChar( c );
break; break;
case EscapedName: case EscapedName:
endMode(); endMode();
m_substring += c; addCharToPattern(c);
m_patternName += c; return true;
m_realPatternPos++;
return;
default: default:
case Tag: case Tag:
case QuotedName: case QuotedName:
if( processOtherChar( c ) ) if( processOtherChar( c ) )
return; return true;
break; break;
} }
@ -69,6 +68,7 @@ namespace Catch {
m_patternName += c; m_patternName += c;
m_realPatternPos++; m_realPatternPos++;
} }
return true;
} }
// Two of the processing methods return true to signal the caller to return // Two of the processing methods return true to signal the caller to return
// without adding the given character to the current pattern strings // without adding the given character to the current pattern strings
@ -161,6 +161,20 @@ namespace Catch {
m_mode = lastMode; m_mode = lastMode;
} }
bool TestSpecParser::separate() {
if( (m_mode==QuotedName) || (m_mode==Tag) ){
//invalid argument, signal failure to previous scope.
m_mode = None;
m_pos = m_arg.size();
m_substring.clear();
m_patternName.clear();
return false;
}
endMode();
addFilter();
return true; //success
}
TestSpec parseTestSpec( std::string const& arg ) { TestSpec parseTestSpec( std::string const& arg ) {
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
} }

View File

@ -41,7 +41,7 @@ namespace Catch {
TestSpec testSpec(); TestSpec testSpec();
private: private:
void visitChar( char c ); bool visitChar( char c );
void startNewMode( Mode mode ); void startNewMode( Mode mode );
bool processNoneChar( char c ); bool processNoneChar( char c );
void processNameChar( char c ); void processNameChar( char c );
@ -51,6 +51,8 @@ namespace Catch {
bool isControlChar( char c ) const; bool isControlChar( char c ) const;
void saveLastMode(); void saveLastMode();
void revertBackToLastMode(); void revertBackToLastMode();
void addFilter();
bool separate();
template<typename T> template<typename T>
void addPattern() { void addPattern() {
@ -74,7 +76,12 @@ namespace Catch {
m_mode = None; m_mode = None;
} }
void addFilter(); inline void addCharToPattern(char c) {
m_substring += c;
m_patternName += c;
m_realPatternPos++;
}
}; };
TestSpec parseTestSpec( std::string const& arg ); TestSpec parseTestSpec( std::string const& arg );

View File

@ -51,6 +51,8 @@ namespace Catch {
void noMatchingTestCases(std::string const&) override {} void noMatchingTestCases(std::string const&) override {}
void reportInvalidArguments(std::string const&) override {}
void testRunStarting(TestRunInfo const& _testRunInfo) override { void testRunStarting(TestRunInfo const& _testRunInfo) override {
currentTestRunInfo = _testRunInfo; currentTestRunInfo = _testRunInfo;
} }

View File

@ -383,6 +383,10 @@ void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
stream << "No test cases matched '" << spec << '\'' << std::endl; stream << "No test cases matched '" << spec << '\'' << std::endl;
} }
void ConsoleReporter::reportInvalidArguments(std::string const&arg){
stream << "Invalid Filter: " << arg << std::endl;
}
void ConsoleReporter::assertionStarting(AssertionInfo const&) {} void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {

View File

@ -32,6 +32,8 @@ namespace Catch {
void noMatchingTestCases(std::string const& spec) override; void noMatchingTestCases(std::string const& spec) override;
void reportInvalidArguments(std::string const&arg) override;
void assertionStarting(AssertionInfo const&) override; void assertionStarting(AssertionInfo const&) override;
bool assertionEnded(AssertionStats const& _assertionStats) override; bool assertionEnded(AssertionStats const& _assertionStats) override;

View File

@ -42,6 +42,13 @@ namespace Catch {
m_reporter->noMatchingTestCases( spec ); m_reporter->noMatchingTestCases( spec );
} }
void ListeningReporter::reportInvalidArguments(std::string const&arg){
for ( auto const& listener : m_listeners ) {
listener->reportInvalidArguments( arg );
}
m_reporter->reportInvalidArguments( arg );
}
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
void ListeningReporter::benchmarkPreparing( std::string const& name ) { void ListeningReporter::benchmarkPreparing( std::string const& name ) {
for (auto const& listener : m_listeners) { for (auto const& listener : m_listeners) {

View File

@ -29,6 +29,8 @@ namespace Catch {
void noMatchingTestCases( std::string const& spec ) override; void noMatchingTestCases( std::string const& spec ) override;
void reportInvalidArguments(std::string const&arg) override;
static std::set<Verbosity> getSupportedVerbosities(); static std::set<Verbosity> getSupportedVerbosities();
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)