Split [.foo] into [.][foo] when parsing test specs

b77cec05c0 fixed this problem for tagging tests, so that a test
case tagged with `[.foo]` would be parsed as tagged with `[.][foo]`.
This does the same for the test spec parsing.

Fixes #1798
This commit is contained in:
Martin Hořeňovský 2019-11-05 23:28:47 +01:00
parent c409dccee5
commit 930f49a641
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
9 changed files with 201 additions and 42 deletions

View File

@ -20,10 +20,10 @@ 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 )
//if visitChar fails //if visitChar fails
if( !visitChar( m_arg[m_pos] ) ){ if( !visitChar( m_arg[m_pos] ) ){
m_testSpec.m_invalidArgs.push_back(arg); m_testSpec.m_invalidArgs.push_back(arg);
break; break;
} }
@ -113,9 +113,9 @@ namespace Catch {
switch( m_mode ) { switch( m_mode ) {
case Name: case Name:
case QuotedName: case QuotedName:
return addPattern<TestSpec::NamePattern>(); return addNamePattern();
case Tag: case Tag:
return addPattern<TestSpec::TagPattern>(); return addTagPattern();
case EscapedName: case EscapedName:
revertBackToLastMode(); revertBackToLastMode();
return; return;
@ -156,12 +156,12 @@ namespace Catch {
void TestSpecParser::saveLastMode() { void TestSpecParser::saveLastMode() {
lastMode = m_mode; lastMode = m_mode;
} }
void TestSpecParser::revertBackToLastMode() { void TestSpecParser::revertBackToLastMode() {
m_mode = lastMode; m_mode = lastMode;
} }
bool TestSpecParser::separate() { bool TestSpecParser::separate() {
if( (m_mode==QuotedName) || (m_mode==Tag) ){ if( (m_mode==QuotedName) || (m_mode==Tag) ){
//invalid argument, signal failure to previous scope. //invalid argument, signal failure to previous scope.
m_mode = None; m_mode = None;
@ -174,7 +174,63 @@ namespace Catch {
addFilter(); addFilter();
return true; //success return true; //success
} }
std::string TestSpecParser::preprocessPattern() {
std::string token = m_patternName;
for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
m_escapeChars.clear();
if (startsWith(token, "exclude:")) {
m_exclusion = true;
token = token.substr(8);
}
m_patternName.clear();
return token;
}
void TestSpecParser::addNamePattern() {
auto token = preprocessPattern();
if (!token.empty()) {
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
if (m_exclusion)
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
m_currentFilter.m_patterns.push_back(pattern);
}
m_substring.clear();
m_exclusion = false;
m_mode = None;
}
void TestSpecParser::addTagPattern() {
auto token = preprocessPattern();
if (!token.empty()) {
// If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
// we have to create a separate hide tag and shorten the real one
if (token.size() > 1 && token[0] == '.') {
token.erase(token.begin());
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
if (m_exclusion) {
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
}
m_currentFilter.m_patterns.push_back(pattern);
}
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
if (m_exclusion) {
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
}
m_currentFilter.m_patterns.push_back(pattern);
}
m_substring.clear();
m_exclusion = false;
m_mode = None;
}
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

@ -53,35 +53,20 @@ namespace Catch {
void revertBackToLastMode(); void revertBackToLastMode();
void addFilter(); void addFilter();
bool separate(); bool separate();
template<typename T> // Handles common preprocessing of the pattern for name/tag patterns
void addPattern() { std::string preprocessPattern();
std::string token = m_patternName; // Adds the current pattern as a test name
for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) void addNamePattern();
token = token.substr( 0, m_escapeChars[i] - i ) + token.substr( m_escapeChars[i] -i +1 ); // Adds the current pattern as a tag
m_escapeChars.clear(); void addTagPattern();
if( startsWith( token, "exclude:" ) ) {
m_exclusion = true;
token = token.substr( 8 );
}
if( !token.empty() ) {
TestSpec::PatternPtr pattern = std::make_shared<T>( token, m_substring );
if( m_exclusion )
pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
m_currentFilter.m_patterns.push_back( pattern );
}
m_substring.clear();
m_patternName.clear();
m_exclusion = false;
m_mode = None;
}
inline void addCharToPattern(char c) { inline void addCharToPattern(char c) {
m_substring += c; m_substring += c;
m_patternName += c; m_patternName += c;
m_realPatternPos++; m_realPatternPos++;
} }
}; };
TestSpec parseTestSpec( std::string const& arg ); TestSpec parseTestSpec( std::string const& arg );

View File

@ -1027,6 +1027,12 @@ CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( " aardvark
CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( " aardvark " ) ) for: true CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( " aardvark " ) ) for: true
CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( "aardvark " ) ) for: true CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( "aardvark " ) ) for: true
CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( "aardvark" ) ) for: true CmdLine.tests.cpp:<line number>: passed: spec.matches( fakeTestCase( "aardvark" ) ) for: true
CmdLine.tests.cpp:<line number>: passed: spec.matches(fakeTestCase("hidden and foo", "[.][foo]")) for: true
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("only foo", "[foo]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("hidden and foo", "[.][foo]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("only foo", "[foo]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: !(spec.matches(fakeTestCase("only hidden", "[.]"))) for: !false
CmdLine.tests.cpp:<line number>: passed: spec.matches(fakeTestCase("neither foo nor hidden", "[bar]")) for: true
Condition.tests.cpp:<line number>: passed: p == 0 for: 0 == 0 Condition.tests.cpp:<line number>: passed: p == 0 for: 0 == 0
Condition.tests.cpp:<line number>: passed: p == pNULL for: 0 == 0 Condition.tests.cpp:<line number>: passed: p == pNULL for: 0 == 0
Condition.tests.cpp:<line number>: passed: p != 0 for: 0x<hex digits> != 0 Condition.tests.cpp:<line number>: passed: p != 0 for: 0x<hex digits> != 0

View File

@ -1381,5 +1381,5 @@ due to unexpected exception with message:
=============================================================================== ===============================================================================
test cases: 304 | 230 passed | 70 failed | 4 failed as expected test cases: 304 | 230 passed | 70 failed | 4 failed as expected
assertions: 1653 | 1501 passed | 131 failed | 21 failed as expected assertions: 1659 | 1507 passed | 131 failed | 21 failed as expected

View File

@ -7361,6 +7361,50 @@ CmdLine.tests.cpp:<line number>: PASSED:
with expansion: with expansion:
true true
-------------------------------------------------------------------------------
Parse test names and tags
Shortened hide tags are split apart when parsing
-------------------------------------------------------------------------------
CmdLine.tests.cpp:<line number>
...............................................................................
CmdLine.tests.cpp:<line number>: PASSED:
CHECK( spec.matches(fakeTestCase("hidden and foo", "[.][foo]")) )
with expansion:
true
CmdLine.tests.cpp:<line number>: PASSED:
CHECK_FALSE( spec.matches(fakeTestCase("only foo", "[foo]")) )
with expansion:
!false
-------------------------------------------------------------------------------
Parse test names and tags
Shortened hide tags also properly handle exclusion
-------------------------------------------------------------------------------
CmdLine.tests.cpp:<line number>
...............................................................................
CmdLine.tests.cpp:<line number>: PASSED:
CHECK_FALSE( spec.matches(fakeTestCase("hidden and foo", "[.][foo]")) )
with expansion:
!false
CmdLine.tests.cpp:<line number>: PASSED:
CHECK_FALSE( spec.matches(fakeTestCase("only foo", "[foo]")) )
with expansion:
!false
CmdLine.tests.cpp:<line number>: PASSED:
CHECK_FALSE( spec.matches(fakeTestCase("only hidden", "[.]")) )
with expansion:
!false
CmdLine.tests.cpp:<line number>: PASSED:
CHECK( spec.matches(fakeTestCase("neither foo nor hidden", "[bar]")) )
with expansion:
true
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Pointers can be compared to null Pointers can be compared to null
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -13208,5 +13252,5 @@ Misc.tests.cpp:<line number>: PASSED:
=============================================================================== ===============================================================================
test cases: 304 | 214 passed | 86 failed | 4 failed as expected test cases: 304 | 214 passed | 86 failed | 4 failed as expected
assertions: 1670 | 1501 passed | 148 failed | 21 failed as expected assertions: 1676 | 1507 passed | 148 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact <testsuitesloose text artifact
> >
<testsuite name="<exe-name>" errors="17" failures="132" tests="1671" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> <testsuite name="<exe-name>" errors="17" failures="132" tests="1677" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties> <properties>
<property name="filters" value="~[!nonportable]~[!benchmark]~[approvals]"/> <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals]"/>
<property name="random-seed" value="1"/> <property name="random-seed" value="1"/>
@ -982,6 +982,8 @@ Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Parse test names and tags/quoted string followed by tag exclusion" time="{duration}"/> <testcase classname="<exe-name>.global" name="Parse test names and tags/quoted string followed by tag exclusion" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Parse test names and tags/Leading and trailing spaces in test spec" time="{duration}"/> <testcase classname="<exe-name>.global" name="Parse test names and tags/Leading and trailing spaces in test spec" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Parse test names and tags/Leading and trailing spaces in test name" time="{duration}"/> <testcase classname="<exe-name>.global" name="Parse test names and tags/Leading and trailing spaces in test name" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Parse test names and tags/Shortened hide tags are split apart when parsing" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Parse test names and tags/Shortened hide tags also properly handle exclusion" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Pointers can be compared to null" time="{duration}"/> <testcase classname="<exe-name>.global" name="Pointers can be compared to null" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Precision of floating point stringification can be set/Floats" time="{duration}"/> <testcase classname="<exe-name>.global" name="Precision of floating point stringification can be set/Floats" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Precision of floating point stringification can be set/Double" time="{duration}"/> <testcase classname="<exe-name>.global" name="Precision of floating point stringification can be set/Double" time="{duration}"/>

View File

@ -35,6 +35,8 @@
<testCase name="Parse test names and tags/quoted string followed by tag exclusion" duration="{duration}"/> <testCase name="Parse test names and tags/quoted string followed by tag exclusion" duration="{duration}"/>
<testCase name="Parse test names and tags/Leading and trailing spaces in test spec" duration="{duration}"/> <testCase name="Parse test names and tags/Leading and trailing spaces in test spec" duration="{duration}"/>
<testCase name="Parse test names and tags/Leading and trailing spaces in test name" duration="{duration}"/> <testCase name="Parse test names and tags/Leading and trailing spaces in test name" duration="{duration}"/>
<testCase name="Parse test names and tags/Shortened hide tags are split apart when parsing" duration="{duration}"/>
<testCase name="Parse test names and tags/Shortened hide tags also properly handle exclusion" duration="{duration}"/>
<testCase name="Process can be configured on command line/empty args don't cause a crash" duration="{duration}"/> <testCase name="Process can be configured on command line/empty args don't cause a crash" duration="{duration}"/>
<testCase name="Process can be configured on command line/default - no arguments" duration="{duration}"/> <testCase name="Process can be configured on command line/default - no arguments" duration="{duration}"/>
<testCase name="Process can be configured on command line/test lists/Specify one test case using" duration="{duration}"/> <testCase name="Process can be configured on command line/test lists/Specify one test case using" duration="{duration}"/>

View File

@ -8019,7 +8019,7 @@ Nor would this
</Section> </Section>
<OverallResult success="false"/> <OverallResult success="false"/>
</TestCase> </TestCase>
<TestCase name="Parse test names and tags" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" > <TestCase name="Parse test names and tags" tags="[command-line][test-spec]" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Section name="Empty test spec should have no filters" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" > <Section name="Empty test spec should have no filters" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Expression success="true" type="CHECK" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" > <Expression success="true" type="CHECK" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original> <Original>
@ -9279,6 +9279,60 @@ Nor would this
</Expression> </Expression>
<OverallResults successes="5" failures="0" expectedFailures="0"/> <OverallResults successes="5" failures="0" expectedFailures="0"/>
</Section> </Section>
<Section name="Shortened hide tags are split apart when parsing" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Expression success="true" type="CHECK" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
spec.matches(fakeTestCase("hidden and foo", "[.][foo]"))
</Original>
<Expanded>
true
</Expanded>
</Expression>
<Expression success="true" type="CHECK_FALSE" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
!(spec.matches(fakeTestCase("only foo", "[foo]")))
</Original>
<Expanded>
!false
</Expanded>
</Expression>
<OverallResults successes="2" failures="0" expectedFailures="0"/>
</Section>
<Section name="Shortened hide tags also properly handle exclusion" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Expression success="true" type="CHECK_FALSE" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
!(spec.matches(fakeTestCase("hidden and foo", "[.][foo]")))
</Original>
<Expanded>
!false
</Expanded>
</Expression>
<Expression success="true" type="CHECK_FALSE" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
!(spec.matches(fakeTestCase("only foo", "[foo]")))
</Original>
<Expanded>
!false
</Expanded>
</Expression>
<Expression success="true" type="CHECK_FALSE" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
!(spec.matches(fakeTestCase("only hidden", "[.]")))
</Original>
<Expanded>
!false
</Expanded>
</Expression>
<Expression success="true" type="CHECK" filename="projects/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
<Original>
spec.matches(fakeTestCase("neither foo nor hidden", "[bar]"))
</Original>
<Expanded>
true
</Expanded>
</Expression>
<OverallResults successes="4" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<TestCase name="Pointers can be compared to null" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" > <TestCase name="Pointers can be compared to null" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
@ -15790,7 +15844,7 @@ loose text artifact
</Section> </Section>
<OverallResult success="true"/> <OverallResult success="true"/>
</TestCase> </TestCase>
<OverallResults successes="1501" failures="149" expectedFailures="21"/> <OverallResults successes="1507" failures="149" expectedFailures="21"/>
</Group> </Group>
<OverallResults successes="1501" failures="148" expectedFailures="21"/> <OverallResults successes="1507" failures="148" expectedFailures="21"/>
</Catch> </Catch>

View File

@ -17,7 +17,7 @@
inline Catch::TestCase fakeTestCase(const char* name, const char* desc = "") { return Catch::makeTestCase(nullptr, "", { name, desc }, CATCH_INTERNAL_LINEINFO); } inline Catch::TestCase fakeTestCase(const char* name, const char* desc = "") { return Catch::makeTestCase(nullptr, "", { name, desc }, CATCH_INTERNAL_LINEINFO); }
TEST_CASE( "Parse test names and tags" ) { TEST_CASE( "Parse test names and tags", "[command-line][test-spec]" ) {
using Catch::parseTestSpec; using Catch::parseTestSpec;
using Catch::TestSpec; using Catch::TestSpec;
@ -269,7 +269,6 @@ TEST_CASE( "Parse test names and tags" ) {
CHECK( spec.matches( fakeTestCase( " aardvark " ) ) ); CHECK( spec.matches( fakeTestCase( " aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark " ) ) ); CHECK( spec.matches( fakeTestCase( "aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark" ) ) ); CHECK( spec.matches( fakeTestCase( "aardvark" ) ) );
} }
SECTION( "Leading and trailing spaces in test name" ) { SECTION( "Leading and trailing spaces in test name" ) {
TestSpec spec = parseTestSpec( "aardvark" ); TestSpec spec = parseTestSpec( "aardvark" );
@ -278,7 +277,18 @@ TEST_CASE( "Parse test names and tags" ) {
CHECK( spec.matches( fakeTestCase( " aardvark " ) ) ); CHECK( spec.matches( fakeTestCase( " aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark " ) ) ); CHECK( spec.matches( fakeTestCase( "aardvark " ) ) );
CHECK( spec.matches( fakeTestCase( "aardvark" ) ) ); CHECK( spec.matches( fakeTestCase( "aardvark" ) ) );
}
SECTION("Shortened hide tags are split apart when parsing") {
TestSpec spec = parseTestSpec("[.foo]");
CHECK(spec.matches(fakeTestCase("hidden and foo", "[.][foo]")));
CHECK_FALSE(spec.matches(fakeTestCase("only foo", "[foo]")));
}
SECTION("Shortened hide tags also properly handle exclusion") {
TestSpec spec = parseTestSpec("~[.foo]");
CHECK_FALSE(spec.matches(fakeTestCase("hidden and foo", "[.][foo]")));
CHECK_FALSE(spec.matches(fakeTestCase("only foo", "[foo]")));
CHECK_FALSE(spec.matches(fakeTestCase("only hidden", "[.]")));
CHECK(spec.matches(fakeTestCase("neither foo nor hidden", "[bar]")));
} }
} }
@ -486,7 +496,7 @@ TEST_CASE( "Process can be configured on command line", "[config][command-line]"
REQUIRE(config.benchmarkSamples == 200); REQUIRE(config.benchmarkSamples == 200);
} }
SECTION("resamples") { SECTION("resamples") {
CHECK(cli.parse({ "test", "--benchmark-resamples=20000" })); CHECK(cli.parse({ "test", "--benchmark-resamples=20000" }));