Add support for retrieving generator's element as string

This commit is contained in:
Martin Hořeňovský 2022-05-16 16:34:06 +02:00
parent af8b54ecd5
commit 48f3226974
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
5 changed files with 153 additions and 7 deletions

View File

@ -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

View File

@ -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;

View 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

View File

@ -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>;

View File

@ -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?