&& generator):
+ m_generator(std::move(generator)),
+ m_predicate(std::forward(pred))
+ {
+ if (!m_predicate(m_generator.get())) {
+ // It might happen that there are no values that pass the
+ // filter. In that case we throw an exception.
+ auto has_initial_value = next();
+ if (!has_initial_value) {
+ Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
+ }
+ }
+ }
- Generators() : GeneratorBase( 0 ) {}
+ T const& get() const override {
+ return m_generator.get();
+ }
- void populate( T&& val ) {
- m_size += 1;
- m_generators.emplace_back( value( std::move( val ) ) );
+ bool next() override {
+ bool success = m_generator.next();
+ if (!success) {
+ return false;
+ }
+ while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
+ return success;
}
- template
- void populate( U&& val ) {
- populate( T( std::move( val ) ) );
+ };
+
+
+ template
+ GeneratorWrapper filter(Predicate&& pred, GeneratorWrapper&& generator) {
+ return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator))));
+ }
+
+ template
+ class RepeatGenerator : public IGenerator {
+ GeneratorWrapper m_generator;
+ mutable std::vector m_returned;
+ size_t m_target_repeats;
+ size_t m_current_repeat = 0;
+ size_t m_repeat_index = 0;
+ public:
+ RepeatGenerator(size_t repeats, GeneratorWrapper&& generator):
+ m_generator(std::move(generator)),
+ m_target_repeats(repeats)
+ {
+ assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
}
- void populate( Generator&& generator ) {
- m_size += generator.size();
- m_generators.emplace_back( std::move( generator ) );
+
+ T const& get() const override {
+ if (m_current_repeat == 0) {
+ m_returned.push_back(m_generator.get());
+ return m_returned.back();
+ }
+ return m_returned[m_repeat_index];
}
- template
- void populate( U&& valueOrGenerator, Gs... moreGenerators ) {
- populate( std::forward( valueOrGenerator ) );
- populate( std::forward( moreGenerators )... );
- }
-
- auto operator[]( size_t index ) const -> T {
- size_t sizes = 0;
- for( auto const& gen : m_generators ) {
- auto localIndex = index-sizes;
- sizes += gen.size();
- if( index < sizes )
- return gen[localIndex];
+ bool next() override {
+ // There are 2 basic cases:
+ // 1) We are still reading the generator
+ // 2) We are reading our own cache
+
+ // In the first case, we need to poke the underlying generator.
+ // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
+ if (m_current_repeat == 0) {
+ const auto success = m_generator.next();
+ if (!success) {
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
}
- CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
+
+ // In the second case, we need to move indices forward and check that we haven't run up against the end
+ ++m_repeat_index;
+ if (m_repeat_index == m_returned.size()) {
+ m_repeat_index = 0;
+ ++m_current_repeat;
+ }
+ return m_current_repeat < m_target_repeats;
}
};
- template
- auto makeGenerators( Generator&& generator, Gs... moreGenerators ) -> Generators {
- Generators generators;
- generators.m_generators.reserve( 1+sizeof...(Gs) );
- generators.populate( std::move( generator ), std::forward( moreGenerators )... );
- return generators;
+ template
+ GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) {
+ return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator)));
}
- template
- auto makeGenerators( Generator&& generator ) -> Generators {
- Generators generators;
- generators.populate( std::move( generator ) );
- return generators;
- }
- template
- auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators {
- return makeGenerators( value( std::forward( val ) ), std::forward( moreGenerators )... );
+
+ template
+ class MapGenerator : public IGenerator {
+ // TBD: provide static assert for mapping function, for friendly error message
+ GeneratorWrapper m_generator;
+ Func m_function;
+ // To avoid returning dangling reference, we have to save the values
+ T m_cache;
+ public:
+ template
+ MapGenerator(F2&& function, GeneratorWrapper&& generator) :
+ m_generator(std::move(generator)),
+ m_function(std::forward(function)),
+ m_cache(m_function(m_generator.get()))
+ {}
+
+ T const& get() const override {
+ return m_cache;
+ }
+ bool next() override {
+ const auto success = m_generator.next();
+ if (success) {
+ m_cache = m_function(m_generator.get());
+ }
+ return success;
+ }
+ };
+
+ template
+ GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) {
+ return GeneratorWrapper(
+ pf::make_unique>(std::forward(function), std::move(generator))
+ );
}
- template
- auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators {
- return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... );
+ template
+ GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) {
+ return GeneratorWrapper(
+ pf::make_unique>(std::forward(function), std::move(generator))
+ );
}
-
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
template
// Note: The type after -> is weird, because VS2015 cannot parse
// the expression used in the typedef inside, when it is in
// return type. Yeah, ¯\_(ツ)_/¯
- auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval()[0]) {
+ auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) {
using UnderlyingType = typename decltype(generatorExpression())::type;
IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
- if( !tracker.hasGenerator() )
- tracker.setGenerator( pf::make_unique>( generatorExpression() ) );
+ if (!tracker.hasGenerator()) {
+ tracker.setGenerator(pf::make_unique>(generatorExpression()));
+ }
- auto const& generator = static_cast const&>( *tracker.getGenerator() );
- return generator[tracker.getIndex()];
+ auto const& generator = static_cast const&>( *tracker.getGenerator() );
+ return generator.get();
}
} // namespace Generators
diff --git a/include/internal/catch_interfaces_generatortracker.h b/include/internal/catch_interfaces_generatortracker.h
index 2cf6269e4d..c1b1391ff1 100644
--- a/include/internal/catch_interfaces_generatortracker.h
+++ b/include/internal/catch_interfaces_generatortracker.h
@@ -13,16 +13,17 @@
namespace Catch {
namespace Generators {
- class GeneratorBase {
- protected:
- size_t m_size = 0;
-
+ class GeneratorUntypedBase {
public:
- GeneratorBase( size_t size ) : m_size( size ) {}
- virtual ~GeneratorBase();
- auto size() const -> size_t { return m_size; }
+ GeneratorUntypedBase() = default;
+ virtual ~GeneratorUntypedBase();
+ // Attempts to move the generator to the next element
+ //
+ // Returns true iff the move succeeded (and a valid element
+ // can be retrieved).
+ virtual bool next() = 0;
};
- using GeneratorBasePtr = std::unique_ptr;
+ using GeneratorBasePtr = std::unique_ptr;
} // namespace Generators
@@ -31,7 +32,6 @@ namespace Catch {
virtual auto hasGenerator() const -> bool = 0;
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
- virtual auto getIndex() const -> std::size_t = 0;
};
} // namespace Catch
diff --git a/include/internal/catch_run_context.cpp b/include/internal/catch_run_context.cpp
index ffdd2ebbf6..77dcaae072 100644
--- a/include/internal/catch_run_context.cpp
+++ b/include/internal/catch_run_context.cpp
@@ -14,7 +14,6 @@ namespace Catch {
namespace Generators {
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
- size_t m_index = static_cast( -1 );
GeneratorBasePtr m_generator;
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
@@ -28,7 +27,7 @@ namespace Catch {
ITracker& currentTracker = ctx.currentTracker();
if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
- assert( childTracker->isIndexTracker() );
+ assert( childTracker->isGeneratorTracker() );
tracker = std::static_pointer_cast( childTracker );
}
else {
@@ -37,28 +36,24 @@ namespace Catch {
}
if( !ctx.completedCycle() && !tracker->isComplete() ) {
- if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
- tracker->moveNext();
tracker->open();
}
return *tracker;
}
- void moveNext() {
- m_index++;
- m_children.clear();
- }
-
// TrackerBase interface
- bool isIndexTracker() const override { return true; }
+ bool isGeneratorTracker() const override { return true; }
auto hasGenerator() const -> bool override {
return !!m_generator;
}
void close() override {
TrackerBase::close();
- if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 )
+ // Generator interface only finds out if it has another item on atual move
+ if (m_runState == CompletedSuccessfully && m_generator->next()) {
+ m_children.clear();
m_runState = Executing;
+ }
}
// IGeneratorTracker interface
@@ -68,9 +63,6 @@ namespace Catch {
void setGenerator( GeneratorBasePtr&& generator ) override {
m_generator = std::move( generator );
}
- auto getIndex() const -> size_t override {
- return m_index;
- }
};
GeneratorTracker::~GeneratorTracker() {}
}
diff --git a/include/internal/catch_test_case_tracker.cpp b/include/internal/catch_test_case_tracker.cpp
index f6e5ac081d..210f273052 100644
--- a/include/internal/catch_test_case_tracker.cpp
+++ b/include/internal/catch_test_case_tracker.cpp
@@ -121,7 +121,7 @@ namespace TestCaseTracking {
}
bool TrackerBase::isSectionTracker() const { return false; }
- bool TrackerBase::isIndexTracker() const { return false; }
+ bool TrackerBase::isGeneratorTracker() const { return false; }
void TrackerBase::open() {
m_runState = Executing;
diff --git a/include/internal/catch_test_case_tracker.h b/include/internal/catch_test_case_tracker.h
index e873d788e2..172760017d 100644
--- a/include/internal/catch_test_case_tracker.h
+++ b/include/internal/catch_test_case_tracker.h
@@ -54,7 +54,7 @@ namespace TestCaseTracking {
// Debug/ checking
virtual bool isSectionTracker() const = 0;
- virtual bool isIndexTracker() const = 0;
+ virtual bool isGeneratorTracker() const = 0;
};
class TrackerContext {
@@ -120,7 +120,7 @@ namespace TestCaseTracking {
void openChild() override;
bool isSectionTracker() const override;
- bool isIndexTracker() const override;
+ bool isGeneratorTracker() const override;
void open();
diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt
index 72627f1fb5..615e368b35 100644
--- a/projects/SelfTest/Baselines/compact.sw.approved.txt
+++ b/projects/SelfTest/Baselines/compact.sw.approved.txt
@@ -57,106 +57,87 @@ Tricky.tests.cpp:: passed: !is_true::value for: true
Tricky.tests.cpp:: passed: !!is_true::value for: true
Tricky.tests.cpp:: passed: is_true