mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-11-01 12:47:11 +01:00 
			
		
		
		
	Add support for retrieving generator's element as string
This commit is contained in:
		| @@ -184,6 +184,7 @@ set(IMPL_SOURCES | |||||||
|     ${SOURCES_DIR}/internal/catch_istream.cpp |     ${SOURCES_DIR}/internal/catch_istream.cpp | ||||||
|     ${SOURCES_DIR}/generators/internal/catch_generators_combined_tu.cpp |     ${SOURCES_DIR}/generators/internal/catch_generators_combined_tu.cpp | ||||||
|     ${SOURCES_DIR}/interfaces/catch_interfaces_combined_tu.cpp |     ${SOURCES_DIR}/interfaces/catch_interfaces_combined_tu.cpp | ||||||
|  |     ${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.cpp | ||||||
|     ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp |     ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp | ||||||
|     ${SOURCES_DIR}/internal/catch_list.cpp |     ${SOURCES_DIR}/internal/catch_list.cpp | ||||||
|     ${SOURCES_DIR}/matchers/catch_matchers_floating_point.cpp |     ${SOURCES_DIR}/matchers/catch_matchers_floating_point.cpp | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #ifndef CATCH_GENERATORS_HPP_INCLUDED | #ifndef CATCH_GENERATORS_HPP_INCLUDED | ||||||
| #define CATCH_GENERATORS_HPP_INCLUDED | #define CATCH_GENERATORS_HPP_INCLUDED | ||||||
|  |  | ||||||
|  | #include <catch2/catch_tostring.hpp> | ||||||
| #include <catch2/interfaces/catch_interfaces_generatortracker.hpp> | #include <catch2/interfaces/catch_interfaces_generatortracker.hpp> | ||||||
| #include <catch2/internal/catch_source_line_info.hpp> | #include <catch2/internal/catch_source_line_info.hpp> | ||||||
| #include <catch2/internal/catch_stringref.hpp> | #include <catch2/internal/catch_stringref.hpp> | ||||||
| @@ -32,6 +33,10 @@ namespace Detail { | |||||||
|  |  | ||||||
|     template<typename T> |     template<typename T> | ||||||
|     class IGenerator : public GeneratorUntypedBase { |     class IGenerator : public GeneratorUntypedBase { | ||||||
|  |         std::string stringifyImpl() const override { | ||||||
|  |             return ::Catch::Detail::stringify( get() ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|         ~IGenerator() override = default; |         ~IGenerator() override = default; | ||||||
|         IGenerator() = default; |         IGenerator() = default; | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								src/catch2/interfaces/catch_interfaces_generatortracker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/catch2/interfaces/catch_interfaces_generatortracker.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  |  | ||||||
|  | //              Copyright Catch2 Authors | ||||||
|  | // Distributed under the Boost Software License, Version 1.0. | ||||||
|  | //   (See accompanying file LICENSE_1_0.txt or copy at | ||||||
|  | //        https://www.boost.org/LICENSE_1_0.txt) | ||||||
|  |  | ||||||
|  | // SPDX-License-Identifier: BSL-1.0 | ||||||
|  |  | ||||||
|  | #include <catch2/interfaces/catch_interfaces_generatortracker.hpp> | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | namespace Catch { | ||||||
|  |     namespace Generators { | ||||||
|  |  | ||||||
|  |         bool GeneratorUntypedBase::countedNext() { | ||||||
|  |             auto ret = next(); | ||||||
|  |             if ( ret ) { | ||||||
|  |                 m_stringReprCache.clear(); | ||||||
|  |                 ++m_currentElementIndex; | ||||||
|  |             } | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         StringRef GeneratorUntypedBase::currentElementAsString() const { | ||||||
|  |             if ( m_stringReprCache.empty() ) { | ||||||
|  |                 m_stringReprCache = stringifyImpl(); | ||||||
|  |             } | ||||||
|  |             return m_stringReprCache; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } // namespace Generators | ||||||
|  | } // namespace Catch | ||||||
| @@ -9,11 +9,18 @@ | |||||||
| #define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED | #define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED | ||||||
|  |  | ||||||
| #include <catch2/internal/catch_unique_ptr.hpp> | #include <catch2/internal/catch_unique_ptr.hpp> | ||||||
|  | #include <catch2/internal/catch_stringref.hpp> | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
| namespace Catch { | namespace Catch { | ||||||
|  |  | ||||||
|     namespace Generators { |     namespace Generators { | ||||||
|         class GeneratorUntypedBase { |         class GeneratorUntypedBase { | ||||||
|  |             // Caches result from `toStringImpl`, assume that when it is an | ||||||
|  |             // empty string, the cache is invalidated. | ||||||
|  |             mutable std::string m_stringReprCache; | ||||||
|  |  | ||||||
|             // Counts based on `next` returning true |             // Counts based on `next` returning true | ||||||
|             std::size_t m_currentElementIndex = 0; |             std::size_t m_currentElementIndex = 0; | ||||||
|  |  | ||||||
| @@ -25,6 +32,9 @@ namespace Catch { | |||||||
|              */ |              */ | ||||||
|             virtual bool next() = 0; |             virtual bool next() = 0; | ||||||
|  |  | ||||||
|  |             //! Customization point for `currentElementAsString` | ||||||
|  |             virtual std::string stringifyImpl() const = 0; | ||||||
|  |  | ||||||
|         public: |         public: | ||||||
|             GeneratorUntypedBase() = default; |             GeneratorUntypedBase() = default; | ||||||
|             // Generation of copy ops is deprecated (and Clang will complain) |             // Generation of copy ops is deprecated (and Clang will complain) | ||||||
| @@ -44,15 +54,24 @@ namespace Catch { | |||||||
|              * As with `next`, returns true iff the move succeeded and |              * As with `next`, returns true iff the move succeeded and | ||||||
|              * the generator has new valid element to provide. |              * the generator has new valid element to provide. | ||||||
|              */ |              */ | ||||||
|             bool countedNext() { |             bool countedNext(); | ||||||
|                 auto ret = next(); |  | ||||||
|                 if ( ret ) { |  | ||||||
|                     ++m_currentElementIndex; |  | ||||||
|                 } |  | ||||||
|                 return ret; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             std::size_t currentElementIndex() const { return m_currentElementIndex; } |             std::size_t currentElementIndex() const { return m_currentElementIndex; } | ||||||
|  |  | ||||||
|  |             /** | ||||||
|  |              * Returns generator's current element as user-friendly string. | ||||||
|  |              * | ||||||
|  |              * By default returns string equivalent to calling | ||||||
|  |              * `Catch::Detail::stringify` on the current element, but generators | ||||||
|  |              * can customize their implementation as needed. | ||||||
|  |              * | ||||||
|  |              * Not thread-safe due to internal caching. | ||||||
|  |              * | ||||||
|  |              * The returned ref is valid only until the generator instance | ||||||
|  |              * is destructed, or it moves onto the next element, whichever | ||||||
|  |              * comes first. | ||||||
|  |              */ | ||||||
|  |             StringRef currentElementAsString() const; | ||||||
|         }; |         }; | ||||||
|         using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>; |         using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -424,3 +424,92 @@ TEST_CASE("Generators count returned elements", "[generators][approvals]") { | |||||||
|     REQUIRE_FALSE( generator.countedNext() ); |     REQUIRE_FALSE( generator.countedNext() ); | ||||||
|     REQUIRE( generator.currentElementIndex() == 2 ); |     REQUIRE( generator.currentElementIndex() == 2 ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | TEST_CASE( "Generators can stringify their elements", | ||||||
|  |            "[generators][approvals]" ) { | ||||||
|  |     auto generator = | ||||||
|  |         Catch::Generators::FixedValuesGenerator<int>( { 1, 2, 3 } ); | ||||||
|  |  | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "1"_catch_sr ); | ||||||
|  |     REQUIRE( generator.countedNext() ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "2"_catch_sr ); | ||||||
|  |     REQUIRE( generator.countedNext() ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "3"_catch_sr ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | namespace { | ||||||
|  |     class CustomStringifyGenerator | ||||||
|  |         : public Catch::Generators::IGenerator<bool> { | ||||||
|  |         bool m_first = true; | ||||||
|  |  | ||||||
|  |         std::string stringifyImpl() const override { | ||||||
|  |             return m_first ? "first" : "second"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool next() override { | ||||||
|  |             if ( m_first ) { | ||||||
|  |                 m_first = false; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     public: | ||||||
|  |         bool const& get() const override; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Avoids -Wweak-vtables | ||||||
|  |     bool const& CustomStringifyGenerator::get() const { return m_first; } | ||||||
|  | } // namespace | ||||||
|  |  | ||||||
|  | TEST_CASE( "Generators can override element stringification", | ||||||
|  |            "[generators][approvals]" ) { | ||||||
|  |     CustomStringifyGenerator generator; | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); | ||||||
|  |     REQUIRE( generator.countedNext() ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "second"_catch_sr ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | namespace { | ||||||
|  |     class StringifyCountingGenerator | ||||||
|  |         : public Catch::Generators::IGenerator<bool> { | ||||||
|  |         bool m_first = true; | ||||||
|  |         mutable size_t m_stringificationCalls = 0; | ||||||
|  |  | ||||||
|  |         std::string stringifyImpl() const override { | ||||||
|  |             ++m_stringificationCalls; | ||||||
|  |             return m_first ? "first" : "second"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool next() override { | ||||||
|  |             if ( m_first ) { | ||||||
|  |                 m_first = false; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     public: | ||||||
|  |  | ||||||
|  |         bool const& get() const override; | ||||||
|  |         size_t stringificationCalls() const { return m_stringificationCalls; } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Avoids -Wweak-vtables | ||||||
|  |     bool const& StringifyCountingGenerator::get() const { return m_first; } | ||||||
|  |  | ||||||
|  | } // namespace | ||||||
|  |  | ||||||
|  | TEST_CASE( "Generator element stringification is cached", | ||||||
|  |            "[generators][approvals]" ) { | ||||||
|  |     StringifyCountingGenerator generator; | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); | ||||||
|  |     REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); | ||||||
|  |  | ||||||
|  |     REQUIRE( generator.stringificationCalls() == 1 ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // caching? | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Martin Hořeňovský
					Martin Hořeňovský