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}/generators/internal/catch_generators_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}/internal/catch_list.cpp | ||||
|     ${SOURCES_DIR}/matchers/catch_matchers_floating_point.cpp | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #ifndef CATCH_GENERATORS_HPP_INCLUDED | ||||
| #define CATCH_GENERATORS_HPP_INCLUDED | ||||
|  | ||||
| #include <catch2/catch_tostring.hpp> | ||||
| #include <catch2/interfaces/catch_interfaces_generatortracker.hpp> | ||||
| #include <catch2/internal/catch_source_line_info.hpp> | ||||
| #include <catch2/internal/catch_stringref.hpp> | ||||
| @@ -32,6 +33,10 @@ namespace Detail { | ||||
|  | ||||
|     template<typename T> | ||||
|     class IGenerator : public GeneratorUntypedBase { | ||||
|         std::string stringifyImpl() const override { | ||||
|             return ::Catch::Detail::stringify( get() ); | ||||
|         } | ||||
|  | ||||
|     public: | ||||
|         ~IGenerator() override = 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 | ||||
|  | ||||
| #include <catch2/internal/catch_unique_ptr.hpp> | ||||
| #include <catch2/internal/catch_stringref.hpp> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| namespace Catch { | ||||
|  | ||||
|     namespace Generators { | ||||
|         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 | ||||
|             std::size_t m_currentElementIndex = 0; | ||||
|  | ||||
| @@ -25,6 +32,9 @@ namespace Catch { | ||||
|              */ | ||||
|             virtual bool next() = 0; | ||||
|  | ||||
|             //! Customization point for `currentElementAsString` | ||||
|             virtual std::string stringifyImpl() const = 0; | ||||
|  | ||||
|         public: | ||||
|             GeneratorUntypedBase() = default; | ||||
|             // 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 | ||||
|              * the generator has new valid element to provide. | ||||
|              */ | ||||
|             bool countedNext() { | ||||
|                 auto ret = next(); | ||||
|                 if ( ret ) { | ||||
|                     ++m_currentElementIndex; | ||||
|                 } | ||||
|                 return ret; | ||||
|             } | ||||
|             bool countedNext(); | ||||
|  | ||||
|             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>; | ||||
|  | ||||
|   | ||||
| @@ -424,3 +424,92 @@ TEST_CASE("Generators count returned elements", "[generators][approvals]") { | ||||
|     REQUIRE_FALSE( generator.countedNext() ); | ||||
|     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ý