From c50ba09cdef4e9d8764868a1a67f0ec3850b5d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Tue, 5 Nov 2019 23:28:47 +0100 Subject: [PATCH] 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 --- src/catch2/catch_test_spec_parser.cpp | 61 ++++++++++++++++++- src/catch2/catch_test_spec_parser.h | 28 ++------- .../Baselines/compact.sw.approved.txt | 6 ++ .../Baselines/console.std.approved.txt | 2 +- .../Baselines/console.sw.approved.txt | 46 +++++++++++++- .../SelfTest/Baselines/junit.sw.approved.txt | 4 +- .../Baselines/sonarqube.sw.approved.txt | 2 + tests/SelfTest/Baselines/tap.sw.approved.txt | 22 +++++-- tests/SelfTest/Baselines/xml.sw.approved.txt | 60 +++++++++++++++++- .../IntrospectiveTests/CmdLine.tests.cpp | 15 ++++- 10 files changed, 209 insertions(+), 37 deletions(-) diff --git a/src/catch2/catch_test_spec_parser.cpp b/src/catch2/catch_test_spec_parser.cpp index 5c3df3b6..d8de256c 100644 --- a/src/catch2/catch_test_spec_parser.cpp +++ b/src/catch2/catch_test_spec_parser.cpp @@ -113,9 +113,9 @@ namespace Catch { switch( m_mode ) { case Name: case QuotedName: - return addPattern(); + return addNamePattern(); case Tag: - return addPattern(); + return addTagPattern(); case EscapedName: revertBackToLastMode(); return; @@ -175,6 +175,63 @@ namespace Catch { 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()) { + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(std::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(std::make_unique(token, m_substring)); + } + } + 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()); + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(std::make_unique(".", m_substring)); + m_currentFilter.m_forbidden.emplace_back(std::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(std::make_unique(".", m_substring)); + m_currentFilter.m_required.emplace_back(std::make_unique(token, m_substring)); + } + } + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(std::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(std::make_unique(token, m_substring)); + } + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + TestSpec parseTestSpec( std::string const& arg ) { return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); } diff --git a/src/catch2/catch_test_spec_parser.h b/src/catch2/catch_test_spec_parser.h index 5b20c508..cf978fda 100644 --- a/src/catch2/catch_test_spec_parser.h +++ b/src/catch2/catch_test_spec_parser.h @@ -54,28 +54,12 @@ namespace Catch { void addFilter(); bool separate(); - template - void addPattern() { - 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 ); - } - if( !token.empty() ) { - if (m_exclusion) { - m_currentFilter.m_forbidden.emplace_back(std::make_unique(token, m_substring)); - } else { - m_currentFilter.m_required.emplace_back(std::make_unique(token, m_substring)); - } - } - m_substring.clear(); - m_patternName.clear(); - m_exclusion = false; - m_mode = None; - } + // Handles common preprocessing of the pattern for name/tag patterns + std::string preprocessPattern(); + // Adds the current pattern as a test name + void addNamePattern(); + // Adds the current pattern as a tag + void addTagPattern(); inline void addCharToPattern(char c) { m_substring += c; diff --git a/tests/SelfTest/Baselines/compact.sw.approved.txt b/tests/SelfTest/Baselines/compact.sw.approved.txt index 5f9e6a9f..9dc81ad4 100644 --- a/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/tests/SelfTest/Baselines/compact.sw.approved.txt @@ -1028,6 +1028,12 @@ CmdLine.tests.cpp:: passed: spec.matches( *fakeTestCase( " aardvar CmdLine.tests.cpp:: passed: spec.matches( *fakeTestCase( " aardvark " ) ) for: true CmdLine.tests.cpp:: passed: spec.matches( *fakeTestCase( "aardvark " ) ) for: true CmdLine.tests.cpp:: passed: spec.matches( *fakeTestCase( "aardvark" ) ) for: true +CmdLine.tests.cpp:: passed: spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")) for: true +CmdLine.tests.cpp:: passed: !(spec.matches(*fakeTestCase("only foo", "[foo]"))) for: !false +CmdLine.tests.cpp:: passed: !(spec.matches(*fakeTestCase("hidden and foo", "[.][foo]"))) for: !false +CmdLine.tests.cpp:: passed: !(spec.matches(*fakeTestCase("only foo", "[foo]"))) for: !false +CmdLine.tests.cpp:: passed: !(spec.matches(*fakeTestCase("only hidden", "[.]"))) for: !false +CmdLine.tests.cpp:: passed: spec.matches(*fakeTestCase("neither foo nor hidden", "[bar]")) for: true Condition.tests.cpp:: passed: p == 0 for: 0 == 0 Condition.tests.cpp:: passed: p == pNULL for: 0 == 0 Condition.tests.cpp:: passed: p != 0 for: 0x != 0 diff --git a/tests/SelfTest/Baselines/console.std.approved.txt b/tests/SelfTest/Baselines/console.std.approved.txt index 140a0b8a..fe6e4f14 100644 --- a/tests/SelfTest/Baselines/console.std.approved.txt +++ b/tests/SelfTest/Baselines/console.std.approved.txt @@ -1381,5 +1381,5 @@ due to unexpected exception with message: =============================================================================== test cases: 320 | 246 passed | 70 failed | 4 failed as expected -assertions: 1776 | 1624 passed | 131 failed | 21 failed as expected +assertions: 1782 | 1630 passed | 131 failed | 21 failed as expected diff --git a/tests/SelfTest/Baselines/console.sw.approved.txt b/tests/SelfTest/Baselines/console.sw.approved.txt index ea8481d2..bba11da2 100644 --- a/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/tests/SelfTest/Baselines/console.sw.approved.txt @@ -7370,6 +7370,50 @@ CmdLine.tests.cpp:: PASSED: with expansion: true +------------------------------------------------------------------------------- +Parse test names and tags + Shortened hide tags are split apart when parsing +------------------------------------------------------------------------------- +CmdLine.tests.cpp: +............................................................................... + +CmdLine.tests.cpp:: PASSED: + CHECK( spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")) ) +with expansion: + true + +CmdLine.tests.cpp:: 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: +............................................................................... + +CmdLine.tests.cpp:: PASSED: + CHECK_FALSE( spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")) ) +with expansion: + !false + +CmdLine.tests.cpp:: PASSED: + CHECK_FALSE( spec.matches(*fakeTestCase("only foo", "[foo]")) ) +with expansion: + !false + +CmdLine.tests.cpp:: PASSED: + CHECK_FALSE( spec.matches(*fakeTestCase("only hidden", "[.]")) ) +with expansion: + !false + +CmdLine.tests.cpp:: PASSED: + CHECK( spec.matches(*fakeTestCase("neither foo nor hidden", "[bar]")) ) +with expansion: + true + ------------------------------------------------------------------------------- Pointers can be compared to null ------------------------------------------------------------------------------- @@ -13961,5 +14005,5 @@ Misc.tests.cpp:: PASSED: =============================================================================== test cases: 320 | 230 passed | 86 failed | 4 failed as expected -assertions: 1793 | 1624 passed | 148 failed | 21 failed as expected +assertions: 1799 | 1630 passed | 148 failed | 21 failed as expected diff --git a/tests/SelfTest/Baselines/junit.sw.approved.txt b/tests/SelfTest/Baselines/junit.sw.approved.txt index 336b7a79..f2537378 100644 --- a/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -983,6 +983,8 @@ Message.tests.cpp: + + diff --git a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index b54c147b..fa9e2a57 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -35,6 +35,8 @@ + + diff --git a/tests/SelfTest/Baselines/tap.sw.approved.txt b/tests/SelfTest/Baselines/tap.sw.approved.txt index 1e679cec..1bf32291 100644 --- a/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/tests/SelfTest/Baselines/tap.sw.approved.txt @@ -1978,6 +1978,18 @@ ok {test-number} - spec.matches( *fakeTestCase( " aardvark " ) ) for: true ok {test-number} - spec.matches( *fakeTestCase( "aardvark " ) ) for: true # Parse test names and tags ok {test-number} - spec.matches( *fakeTestCase( "aardvark" ) ) for: true +# Parse test names and tags +ok {test-number} - spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")) for: true +# Parse test names and tags +ok {test-number} - !(spec.matches(*fakeTestCase("only foo", "[foo]"))) for: !false +# Parse test names and tags +ok {test-number} - !(spec.matches(*fakeTestCase("hidden and foo", "[.][foo]"))) for: !false +# Parse test names and tags +ok {test-number} - !(spec.matches(*fakeTestCase("only foo", "[foo]"))) for: !false +# Parse test names and tags +ok {test-number} - !(spec.matches(*fakeTestCase("only hidden", "[.]"))) for: !false +# Parse test names and tags +ok {test-number} - spec.matches(*fakeTestCase("neither foo nor hidden", "[bar]")) for: true # Pointers can be compared to null ok {test-number} - p == 0 for: 0 == 0 # Pointers can be compared to null @@ -2928,9 +2940,9 @@ not ok {test-number} - unexpected exception with message: 'expected exception'; # When unchecked exceptions are thrown from sections they are always failures not ok {test-number} - unexpected exception with message: 'unexpected exception' # Where the LHS is not a simple value -warning 1461 - 'Uncomment the code in this test to check that it gives a sensible compiler error' +warning 1467 - 'Uncomment the code in this test to check that it gives a sensible compiler error' # Where there is more to the expression after the RHS -warning 1462 - 'Uncomment the code in this test to check that it gives a sensible compiler error' +warning 1468 - 'Uncomment the code in this test to check that it gives a sensible compiler error' # X/level/0/a ok {test-number} - # X/level/0/b @@ -3197,9 +3209,9 @@ ok {test-number} - s.result == 17 for: 17 == 17 # measure ok {test-number} - s.iterations == 1 for: 1 == 1 # mix info, unscoped info and warning -warning 1595 - 'info' with 2 messages: 'unscoped info' and 'and warn may mix' +warning 1601 - 'info' with 2 messages: 'unscoped info' and 'and warn may mix' # mix info, unscoped info and warning -warning 1596 - 'info' with 2 messages: 'unscoped info' and 'they are not cleared after warnings' +warning 1602 - 'info' with 2 messages: 'unscoped info' and 'they are not cleared after warnings' # more nested SECTION tests not ok {test-number} - a == b for: 1 == 2 # more nested SECTION tests @@ -3578,5 +3590,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..1785 +1..1791 diff --git a/tests/SelfTest/Baselines/xml.sw.approved.txt b/tests/SelfTest/Baselines/xml.sw.approved.txt index a27f88e5..39122b05 100644 --- a/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/tests/SelfTest/Baselines/xml.sw.approved.txt @@ -8025,7 +8025,7 @@ Nor would this - +
@@ -9285,6 +9285,60 @@ Nor would this
+
+ + + spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")) + + + true + + + + + !(spec.matches(*fakeTestCase("only foo", "[foo]"))) + + + !false + + + +
+
+ + + !(spec.matches(*fakeTestCase("hidden and foo", "[.][foo]"))) + + + !false + + + + + !(spec.matches(*fakeTestCase("only foo", "[foo]"))) + + + !false + + + + + !(spec.matches(*fakeTestCase("only hidden", "[.]"))) + + + !false + + + + + spec.matches(*fakeTestCase("neither foo nor hidden", "[bar]")) + + + true + + + +
@@ -16841,7 +16895,7 @@ loose text artifact - + - + diff --git a/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp b/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp index bff30f2c..68dafad1 100644 --- a/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp +++ b/tests/SelfTest/IntrospectiveTests/CmdLine.tests.cpp @@ -19,7 +19,7 @@ namespace { auto fakeTestCase(const char* name, const char* desc = "") { return Catch::makeTestCaseInfo("", { 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::TestSpec; @@ -280,7 +280,18 @@ TEST_CASE( "Parse test names and tags" ) { 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]"))); } }