&& 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"));
+ }
+ }
+ }
+
+ T const& get() const override {
+ return m_generator.get();
+ }
+
+ 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
+ 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");
+ }
+
+ 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];
+ }
+
+ 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;
+ }
+
+ // 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
+ GeneratorWrapper repeat(size_t repeats, GeneratorWrapper&& generator) {
+ return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator)));
+ }
+
+ 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
+ GeneratorWrapper map(Func&& function, GeneratorWrapper&& generator) {
+ return GeneratorWrapper(
+ pf::make_unique>(std::forward(function), std::move(generator))
+ );
+ }
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
@@ -232,15 +355,16 @@ namespace Generators {
// 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 2cf6269e..c1b1391f 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 ffdd2ebb..77dcaae0 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 f6e5ac08..210f2730 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 e873d788..17276001 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 72627f1f..615e368b 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::value for: true
Tricky.tests.cpp:: passed: !(is_true::value) for: !false
-Generators.tests.cpp:: passed: x < y for: 1 < 101
-Generators.tests.cpp:: passed: x < y for: 1 < 102
-Generators.tests.cpp:: passed: x < y for: 1 < 103
-Generators.tests.cpp:: passed: x < y for: 1 < 104
-Generators.tests.cpp:: passed: x < y for: 1 < 105
-Generators.tests.cpp:: passed: x < y for: 1 < 106
-Generators.tests.cpp:: passed: x < y for: 1 < 107
-Generators.tests.cpp:: passed: x < y for: 1 < 108
-Generators.tests.cpp:: passed: x < y for: 1 < 109
-Generators.tests.cpp: