mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-11-04 14:09:33 +01:00 
			
		
		
		
	Provide round-tripping serialization for TestSpec
This commit is contained in:
		@@ -6,12 +6,14 @@
 | 
			
		||||
 | 
			
		||||
// SPDX-License-Identifier: BSL-1.0
 | 
			
		||||
#include <catch2/catch_test_spec.hpp>
 | 
			
		||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
 | 
			
		||||
#include <catch2/internal/catch_string_manip.hpp>
 | 
			
		||||
#include <catch2/catch_test_case_info.hpp>
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <ostream>
 | 
			
		||||
 | 
			
		||||
namespace Catch {
 | 
			
		||||
 | 
			
		||||
@@ -35,6 +37,10 @@ namespace Catch {
 | 
			
		||||
        return m_wildcardPattern.matches( testCase.name );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TestSpec::NamePattern::serializeTo( std::ostream& out ) const {
 | 
			
		||||
        out << '"' << name() << '"';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
 | 
			
		||||
    : Pattern( filterString )
 | 
			
		||||
@@ -47,6 +53,10 @@ namespace Catch {
 | 
			
		||||
                          Tag( m_tag ) ) != end( testCase.tags );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TestSpec::TagPattern::serializeTo( std::ostream& out ) const {
 | 
			
		||||
        out << name();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
 | 
			
		||||
        bool should_use = !testCase.isHidden();
 | 
			
		||||
        for (auto const& pattern : m_required) {
 | 
			
		||||
@@ -63,18 +73,31 @@ namespace Catch {
 | 
			
		||||
        return should_use;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string TestSpec::Filter::name() const {
 | 
			
		||||
        std::string name;
 | 
			
		||||
        for (auto const& p : m_required) {
 | 
			
		||||
            name += p->name();
 | 
			
		||||
    void TestSpec::Filter::serializeTo( std::ostream& out ) const {
 | 
			
		||||
        bool first = true;
 | 
			
		||||
        for ( auto const& pattern : m_required ) {
 | 
			
		||||
            if ( !first ) {
 | 
			
		||||
                out << ' ';
 | 
			
		||||
            }
 | 
			
		||||
            out << *pattern;
 | 
			
		||||
            first = false;
 | 
			
		||||
        }
 | 
			
		||||
        for (auto const& p : m_forbidden) {
 | 
			
		||||
            name += p->name();
 | 
			
		||||
        for ( auto const& pattern : m_forbidden ) {
 | 
			
		||||
            if ( !first ) {
 | 
			
		||||
                out << ' ';
 | 
			
		||||
            }
 | 
			
		||||
            out << *pattern;
 | 
			
		||||
            first = false;
 | 
			
		||||
        }
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::string TestSpec::extractFilterName( Filter const& filter ) {
 | 
			
		||||
        Catch::ReusableStringStream sstr;
 | 
			
		||||
        sstr << filter;
 | 
			
		||||
        return sstr.str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool TestSpec::hasFilters() const {
 | 
			
		||||
        return !m_filters.empty();
 | 
			
		||||
    }
 | 
			
		||||
@@ -91,7 +114,7 @@ namespace Catch {
 | 
			
		||||
            for( auto const& test : testCases )
 | 
			
		||||
                if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) )
 | 
			
		||||
                    currentMatches.emplace_back( &test );
 | 
			
		||||
            return FilterMatch{ filter.name(), currentMatches };
 | 
			
		||||
            return FilterMatch{ extractFilterName(filter), currentMatches };
 | 
			
		||||
        } );
 | 
			
		||||
        return matches;
 | 
			
		||||
    }
 | 
			
		||||
@@ -100,4 +123,15 @@ namespace Catch {
 | 
			
		||||
        return m_invalidSpecs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TestSpec::serializeTo( std::ostream& out ) const {
 | 
			
		||||
        bool first = true;
 | 
			
		||||
        for ( auto const& filter : m_filters ) {
 | 
			
		||||
            if ( !first ) {
 | 
			
		||||
                out << ',';
 | 
			
		||||
            }
 | 
			
		||||
            out << filter;
 | 
			
		||||
            first = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
#include <catch2/internal/catch_unique_ptr.hpp>
 | 
			
		||||
#include <catch2/internal/catch_wildcard_pattern.hpp>
 | 
			
		||||
 | 
			
		||||
#include <iosfwd>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +35,14 @@ namespace Catch {
 | 
			
		||||
            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
 | 
			
		||||
            std::string const& name() const;
 | 
			
		||||
        private:
 | 
			
		||||
            virtual void serializeTo( std::ostream& out ) const = 0;
 | 
			
		||||
            // Writes string that would be reparsed into the pattern
 | 
			
		||||
            friend std::ostream& operator<<(std::ostream& out,
 | 
			
		||||
                                            Pattern const& pattern) {
 | 
			
		||||
                pattern.serializeTo( out );
 | 
			
		||||
                return out;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            std::string const m_name;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -42,6 +51,8 @@ namespace Catch {
 | 
			
		||||
            explicit NamePattern( std::string const& name, std::string const& filterString );
 | 
			
		||||
            bool matches( TestCaseInfo const& testCase ) const override;
 | 
			
		||||
        private:
 | 
			
		||||
            void serializeTo( std::ostream& out ) const override;
 | 
			
		||||
 | 
			
		||||
            WildcardPattern m_wildcardPattern;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +61,8 @@ namespace Catch {
 | 
			
		||||
            explicit TagPattern( std::string const& tag, std::string const& filterString );
 | 
			
		||||
            bool matches( TestCaseInfo const& testCase ) const override;
 | 
			
		||||
        private:
 | 
			
		||||
            void serializeTo( std::ostream& out ) const override;
 | 
			
		||||
 | 
			
		||||
            std::string m_tag;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -57,10 +70,19 @@ namespace Catch {
 | 
			
		||||
            std::vector<Detail::unique_ptr<Pattern>> m_required;
 | 
			
		||||
            std::vector<Detail::unique_ptr<Pattern>> m_forbidden;
 | 
			
		||||
 | 
			
		||||
            //! Serializes this filter into a string that would be parsed into
 | 
			
		||||
            //! an equivalent filter
 | 
			
		||||
            void serializeTo( std::ostream& out ) const;
 | 
			
		||||
            friend std::ostream& operator<<(std::ostream& out, Filter const& f) {
 | 
			
		||||
                f.serializeTo( out );
 | 
			
		||||
                return out;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool matches( TestCaseInfo const& testCase ) const;
 | 
			
		||||
            std::string name() const;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        static std::string extractFilterName( Filter const& filter );
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        struct FilterMatch {
 | 
			
		||||
            std::string name;
 | 
			
		||||
@@ -77,7 +99,16 @@ namespace Catch {
 | 
			
		||||
    private:
 | 
			
		||||
        std::vector<Filter> m_filters;
 | 
			
		||||
        std::vector<std::string> m_invalidSpecs;
 | 
			
		||||
 | 
			
		||||
        friend class TestSpecParser;
 | 
			
		||||
        //! Serializes this test spec into a string that would be parsed into
 | 
			
		||||
        //! equivalent test spec
 | 
			
		||||
        void serializeTo( std::ostream& out ) const;
 | 
			
		||||
        friend std::ostream& operator<<(std::ostream& out,
 | 
			
		||||
                                        TestSpec const& spec) {
 | 
			
		||||
            spec.serializeTo( out );
 | 
			
		||||
            return out;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -249,7 +249,7 @@ add_test(NAME TestSpecs::CombiningMatchingAndNonMatchingIsOk-1 COMMAND $<TARGET_
 | 
			
		||||
 | 
			
		||||
add_test(NAME TestSpecs::CombiningMatchingAndNonMatchingIsOk-2 COMMAND $<TARGET_FILE:SelfTest> Tracker, "___nonexistent_test___")
 | 
			
		||||
set_tests_properties(TestSpecs::CombiningMatchingAndNonMatchingIsOk-2 PROPERTIES
 | 
			
		||||
    PASS_REGULAR_EXPRESSION "No test cases matched '___nonexistent_test___'"
 | 
			
		||||
    PASS_REGULAR_EXPRESSION "No test cases matched '\"___nonexistent_test___\"'"
 | 
			
		||||
    FAIL_REGULAR_EXPRESSION "No tests ran"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -325,3 +325,40 @@ TEST_CASE("#1912 -- test spec parser handles escaping", "[command-line][test-spe
 | 
			
		||||
        REQUIRE(spec.matches(*fakeTestCase(R"(spec \ char)")));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_CASE("Test spec serialization is round-trippable", "[test-spec][serialization][approvals]") {
 | 
			
		||||
    using Catch::parseTestSpec;
 | 
			
		||||
    using Catch::TestSpec;
 | 
			
		||||
 | 
			
		||||
    auto serializedTestSpec = []( std::string const& spec ) {
 | 
			
		||||
        Catch::ReusableStringStream sstr;
 | 
			
		||||
        sstr << parseTestSpec( spec );
 | 
			
		||||
        return sstr.str();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    SECTION("Spaces are normalized") {
 | 
			
		||||
        CHECK( serializedTestSpec( "[abc][def]" ) == "[abc] [def]" );
 | 
			
		||||
        CHECK( serializedTestSpec( "[def]    [abc]" ) == "[def] [abc]" );
 | 
			
		||||
        CHECK( serializedTestSpec( "[def] [abc]" ) == "[def] [abc]" );
 | 
			
		||||
    }
 | 
			
		||||
    SECTION("Output is order dependent") {
 | 
			
		||||
        CHECK( serializedTestSpec( "[abc][def]" ) == "[abc] [def]" );
 | 
			
		||||
        CHECK( serializedTestSpec( "[def][abc]" ) == "[def] [abc]" );
 | 
			
		||||
    }
 | 
			
		||||
    SECTION("Multiple disjunct filters") {
 | 
			
		||||
        CHECK( serializedTestSpec( "[abc],[def]" ) == "[abc],[def]" );
 | 
			
		||||
        CHECK( serializedTestSpec( "[def],[abc],[idkfa]" ) == "[def],[abc],[idkfa]" );
 | 
			
		||||
    }
 | 
			
		||||
    SECTION("Test names are enclosed in string") {
 | 
			
		||||
        CHECK( serializedTestSpec( "Some test" ) == "\"Some test\"" );
 | 
			
		||||
        CHECK( serializedTestSpec( "*Some test" ) == "\"*Some test\"" );
 | 
			
		||||
        CHECK( serializedTestSpec( "* Some test" ) == "\"* Some test\"" );
 | 
			
		||||
        CHECK( serializedTestSpec( "* Some test *" ) == "\"* Some test *\"" );
 | 
			
		||||
    }
 | 
			
		||||
    SECTION( "Mixing test names and tags" ) {
 | 
			
		||||
        CHECK( serializedTestSpec( "some test[abcd]" ) ==
 | 
			
		||||
               "\"some test\" [abcd]" );
 | 
			
		||||
        CHECK( serializedTestSpec( "[ab]some test[cd]" ) ==
 | 
			
		||||
               "[ab] \"some test\" [cd]" );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user