From 5929d9530cd50c116398f486fea7324055cffe83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Sun, 27 Jan 2019 23:50:43 +0100 Subject: [PATCH] Add generic generator modifiers This means mutiple generic generators and some inference helper * take(n, generator) * filter(predicate, generator) * map(func, generator) * repeat(generator, repeats) --- include/internal/catch_generators.cpp | 4 + include/internal/catch_generators.hpp | 182 +++++++ .../Baselines/compact.sw.approved.txt | 49 ++ .../Baselines/console.std.approved.txt | 4 +- .../Baselines/console.sw.approved.txt | 425 ++++++++++++++- .../SelfTest/Baselines/junit.sw.approved.txt | 13 +- .../SelfTest/Baselines/xml.sw.approved.txt | 501 +++++++++++++++++- .../GeneratorsImpl.tests.cpp | 57 ++ .../SelfTest/UsageTests/Generators.tests.cpp | 52 +- 9 files changed, 1262 insertions(+), 25 deletions(-) diff --git a/include/internal/catch_generators.cpp b/include/internal/catch_generators.cpp index de6dc1fe..5fbe2d23 100644 --- a/include/internal/catch_generators.cpp +++ b/include/internal/catch_generators.cpp @@ -16,6 +16,10 @@ namespace Catch { IGeneratorTracker::~IGeneratorTracker() {} +const char* GeneratorException::what() const noexcept { + return m_msg; +} + namespace Generators { GeneratorUntypedBase::~GeneratorUntypedBase() {} diff --git a/include/internal/catch_generators.hpp b/include/internal/catch_generators.hpp index 92542fde..f7dbc8c7 100644 --- a/include/internal/catch_generators.hpp +++ b/include/internal/catch_generators.hpp @@ -16,8 +16,21 @@ #include #include +#include namespace Catch { + +class GeneratorException : public std::exception { + const char* const m_msg = ""; + +public: + GeneratorException(const char* msg): + m_msg(msg) + {} + + const char* what() const noexcept override final; +}; + namespace Generators { // !TBD move this into its own location? @@ -166,6 +179,175 @@ namespace Generators { return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); } + template + class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + public: + TakeGenerator(size_t target, GeneratorWrapper&& generator): + m_generator(std::move(generator)), + m_target(target) + { + assert(target != 0 && "Empty generators are not allowed"); + } + T const& get() const override { + return m_generator.get(); + } + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template + GeneratorWrapper take(size_t target, GeneratorWrapper&& generator) { + return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); + } + + + template + class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + public: + template + FilterGenerator(P&& pred, GeneratorWrapper&& 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&; diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt index 174adc48..615e368b 100644 --- a/projects/SelfTest/Baselines/compact.sw.approved.txt +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -399,6 +399,24 @@ Matchers.tests.cpp:: passed: WithinAbs(1.f, 0.f) Matchers.tests.cpp:: passed: WithinAbs(1.f, -1.f), std::domain_error Matchers.tests.cpp:: passed: WithinULP(1.f, 0) Matchers.tests.cpp:: passed: WithinULP(1.f, -1), std::domain_error +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i < 4 for: 1 < 4 +Generators.tests.cpp:: passed: i < 4 for: 2 < 4 +Generators.tests.cpp:: passed: i < 4 for: 3 < 4 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: i.size() == 1 for: 1 == 1 +Generators.tests.cpp:: passed: i.size() == 1 for: 1 == 1 +Generators.tests.cpp:: passed: i.size() == 1 for: 1 == 1 +Generators.tests.cpp:: passed: j > 0 for: 1 > 0 +Generators.tests.cpp:: passed: j > 0 for: 2 > 0 +Generators.tests.cpp:: passed: j > 0 for: 3 > 0 +Generators.tests.cpp:: passed: j > 0 for: 1 > 0 +Generators.tests.cpp:: passed: j > 0 for: 2 > 0 +Generators.tests.cpp:: passed: j > 0 for: 3 > 0 Generators.tests.cpp:: passed: j < i for: -3 < 1 Generators.tests.cpp:: passed: j < i for: -2 < 1 Generators.tests.cpp:: passed: j < i for: -1 < 1 @@ -442,6 +460,37 @@ GeneratorsImpl.tests.cpp:: passed: gen.get() == "bb" for: "bb" == " GeneratorsImpl.tests.cpp:: passed: gen.next() for: true GeneratorsImpl.tests.cpp:: passed: gen.get() == "cc" for: "cc" == "cc" GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: gen.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 3 for: 3 == 3 +GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: filter([] (int) { return false; }, value(1)), Catch::GeneratorException +GeneratorsImpl.tests.cpp:: passed: gen.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 2 for: 2 == 2 +GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: gen.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: gen.get() == 2.0 for: 2.0 == 2.0 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 4.0 for: 4.0 == 4.0 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 6.0 for: 6.0 == 6.0 +GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: gen.get() == 3 for: 3 == 3 +GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: gen.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 2 for: 2 == 2 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 3 for: 3 == 3 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 2 for: 2 == 2 +GeneratorsImpl.tests.cpp:: passed: gen.next() for: true +GeneratorsImpl.tests.cpp:: passed: gen.get() == 3 for: 3 == 3 +GeneratorsImpl.tests.cpp:: passed: !(gen.next()) for: !false Approx.tests.cpp:: passed: d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 ) Approx.tests.cpp:: passed: d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 ) Approx.tests.cpp:: passed: !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) diff --git a/projects/SelfTest/Baselines/console.std.approved.txt b/projects/SelfTest/Baselines/console.std.approved.txt index 750596b8..a9ca0277 100644 --- a/projects/SelfTest/Baselines/console.std.approved.txt +++ b/projects/SelfTest/Baselines/console.std.approved.txt @@ -1170,6 +1170,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 244 | 184 passed | 56 failed | 4 failed as expected -assertions: 1248 | 1112 passed | 115 failed | 21 failed as expected +test cases: 245 | 185 passed | 56 failed | 4 failed as expected +assertions: 1297 | 1161 passed | 115 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index bb06d688..92a78c57 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -2850,6 +2850,228 @@ Matchers.tests.cpp:: PASSED: Matchers.tests.cpp:: PASSED: REQUIRE_THROWS_AS( WithinULP(1.f, -1), std::domain_error ) +------------------------------------------------------------------------------- +Generators -- adapters + Filtering by predicate +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Filtering by predicate +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Filtering by predicate +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Shortening a range +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i < 4 ) +with expansion: + 1 < 4 + +------------------------------------------------------------------------------- +Generators -- adapters + Shortening a range +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i < 4 ) +with expansion: + 2 < 4 + +------------------------------------------------------------------------------- +Generators -- adapters + Shortening a range +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i < 4 ) +with expansion: + 3 < 4 + +------------------------------------------------------------------------------- +Generators -- adapters + Transforming elements + Same type +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Transforming elements + Same type +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Transforming elements + Same type +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Transforming elements + Different type +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i.size() == 1 ) +with expansion: + 1 == 1 + +------------------------------------------------------------------------------- +Generators -- adapters + Transforming elements + Different type +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i.size() == 1 ) +with expansion: + 1 == 1 + +------------------------------------------------------------------------------- +Generators -- adapters + Transforming elements + Different type +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( i.size() == 1 ) +with expansion: + 1 == 1 + +------------------------------------------------------------------------------- +Generators -- adapters + Repeating a generator +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( j > 0 ) +with expansion: + 1 > 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Repeating a generator +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( j > 0 ) +with expansion: + 2 > 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Repeating a generator +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( j > 0 ) +with expansion: + 3 > 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Repeating a generator +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( j > 0 ) +with expansion: + 1 > 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Repeating a generator +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( j > 0 ) +with expansion: + 2 > 0 + +------------------------------------------------------------------------------- +Generators -- adapters + Repeating a generator +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( j > 0 ) +with expansion: + 3 > 0 + ------------------------------------------------------------------------------- Generators -- simple one @@ -3219,6 +3441,205 @@ GeneratorsImpl.tests.cpp:: PASSED: with expansion: !false +------------------------------------------------------------------------------- +Generators internals + Filter generator +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 3 ) +with expansion: + 3 == 3 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( gen.next() ) +with expansion: + !false + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_THROWS_AS( filter([] (int) { return false; }, value(1)), Catch::GeneratorException ) + +------------------------------------------------------------------------------- +Generators internals + Take generator + Take less +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 2 ) +with expansion: + 2 == 2 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( gen.next() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Generators internals + Take generator + Take more +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( gen.next() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Generators internals + Map +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 2.0 ) +with expansion: + 2.0 == 2.0 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 4.0 ) +with expansion: + 4.0 == 4.0 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 6.0 ) +with expansion: + 6.0 == 6.0 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( gen.next() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Generators internals + Repeat + Singular repeat +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 3 ) +with expansion: + 3 == 3 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( gen.next() ) +with expansion: + !false + +------------------------------------------------------------------------------- +Generators internals + Repeat + Actual repeat +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 2 ) +with expansion: + 2 == 2 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 3 ) +with expansion: + 3 == 3 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 2 ) +with expansion: + 2 == 2 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( gen.get() == 3 ) +with expansion: + 3 == 3 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( gen.next() ) +with expansion: + !false + ------------------------------------------------------------------------------- Greater-than inequalities with different epsilons ------------------------------------------------------------------------------- @@ -9726,6 +10147,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 244 | 171 passed | 69 failed | 4 failed as expected -assertions: 1262 | 1112 passed | 129 failed | 21 failed as expected +test cases: 245 | 172 passed | 69 failed | 4 failed as expected +assertions: 1311 | 1161 passed | 129 failed | 21 failed as expected diff --git a/projects/SelfTest/Baselines/junit.sw.approved.txt b/projects/SelfTest/Baselines/junit.sw.approved.txt index 3e23fdbd..a245a37a 100644 --- a/projects/SelfTest/Baselines/junit.sw.approved.txt +++ b/projects/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -339,12 +339,23 @@ Message.tests.cpp: + + + + + + + + + + + diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index 6375db17..6558d6f2 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -3538,6 +3538,225 @@ + +

+ + + i % 2 == 0 + + + 0 == 0 + + + +
+
+ + + i % 2 == 0 + + + 0 == 0 + + + +
+
+ + + i % 2 == 0 + + + 0 == 0 + + + +
+
+ + + i < 4 + + + 1 < 4 + + + +
+
+ + + i < 4 + + + 2 < 4 + + + +
+
+ + + i < 4 + + + 3 < 4 + + + +
+
+
+ + + i % 2 == 0 + + + 0 == 0 + + + +
+ +
+
+
+ + + i % 2 == 0 + + + 0 == 0 + + + +
+ +
+
+
+ + + i % 2 == 0 + + + 0 == 0 + + + +
+ +
+
+
+ + + i.size() == 1 + + + 1 == 1 + + + +
+ +
+
+
+ + + i.size() == 1 + + + 1 == 1 + + + +
+ +
+
+
+ + + i.size() == 1 + + + 1 == 1 + + + +
+ +
+
+ + + j > 0 + + + 1 > 0 + + + +
+
+ + + j > 0 + + + 2 > 0 + + + +
+
+ + + j > 0 + + + 3 > 0 + + + +
+
+ + + j > 0 + + + 1 > 0 + + + +
+
+ + + j > 0 + + + 2 > 0 + + + +
+
+ + + j > 0 + + + 3 > 0 + + + +
+ +
@@ -3952,6 +4171,284 @@
+
+ + + gen.get() == 1 + + + 1 == 1 + + + + + gen.next() + + + true + + + + + gen.get() == 3 + + + 3 == 3 + + + + + !(gen.next()) + + + !false + + + + + filter([] (int) { return false; }, value(1)), Catch::GeneratorException + + + filter([] (int) { return false; }, value(1)), Catch::GeneratorException + + + +
+
+
+ + + gen.get() == 1 + + + 1 == 1 + + + + + gen.next() + + + true + + + + + gen.get() == 2 + + + 2 == 2 + + + + + !(gen.next()) + + + !false + + + +
+ +
+
+
+ + + gen.get() == 1 + + + 1 == 1 + + + + + !(gen.next()) + + + !false + + + +
+ +
+
+ + + gen.get() == 2.0 + + + 2.0 == 2.0 + + + + + gen.next() + + + true + + + + + gen.get() == 4.0 + + + 4.0 == 4.0 + + + + + gen.next() + + + true + + + + + gen.get() == 6.0 + + + 6.0 == 6.0 + + + + + !(gen.next()) + + + !false + + + +
+
+
+ + + gen.get() == 3 + + + 3 == 3 + + + + + !(gen.next()) + + + !false + + + +
+ +
+
+
+ + + gen.get() == 1 + + + 1 == 1 + + + + + gen.next() + + + true + + + + + gen.get() == 2 + + + 2 == 2 + + + + + gen.next() + + + true + + + + + gen.get() == 3 + + + 3 == 3 + + + + + gen.next() + + + true + + + + + gen.get() == 1 + + + 1 == 1 + + + + + gen.next() + + + true + + + + + gen.get() == 2 + + + 2 == 2 + + + + + gen.next() + + + true + + + + + gen.get() == 3 + + + 3 == 3 + + + + + !(gen.next()) + + + !false + + + +
+ +
@@ -11654,7 +12151,7 @@ loose text artifact - + - + diff --git a/projects/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp b/projects/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp index 0473b737..0f581947 100644 --- a/projects/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp +++ b/projects/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp @@ -43,4 +43,61 @@ TEST_CASE("Generators internals", "[generators][internals]") { REQUIRE(gen.get() == "cc"); REQUIRE_FALSE(gen.next()); } + SECTION("Filter generator") { + // Normal usage + auto gen = filter([] (int i) { return i != 2; }, values({ 2, 1, 2, 3, 2, 2 })); + REQUIRE(gen.get() == 1); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 3); + REQUIRE_FALSE(gen.next()); + + // Completely filtered-out generator should throw on construction + REQUIRE_THROWS_AS(filter([] (int) { return false; }, value(1)), Catch::GeneratorException); + } + SECTION("Take generator") { + SECTION("Take less") { + auto gen = take(2, values({ 1, 2, 3 })); + REQUIRE(gen.get() == 1); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 2); + REQUIRE_FALSE(gen.next()); + } + SECTION("Take more") { + auto gen = take(2, value(1)); + REQUIRE(gen.get() == 1); + REQUIRE_FALSE(gen.next()); + } + } + SECTION("Map") { + auto gen = map([] (int i) {return 2.0 * i; }, values({ 1, 2, 3 })); + REQUIRE(gen.get() == 2.0); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 4.0); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 6.0); + REQUIRE_FALSE(gen.next()); + } + SECTION("Repeat") { + SECTION("Singular repeat") { + auto gen = repeat(1, value(3)); + REQUIRE(gen.get() == 3); + REQUIRE_FALSE(gen.next()); + } + SECTION("Actual repeat") { + auto gen = repeat(2, values({ 1, 2, 3 })); + REQUIRE(gen.get() == 1); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 2); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 3); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 1); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 2); + REQUIRE(gen.next()); + REQUIRE(gen.get() == 3); + REQUIRE_FALSE(gen.next()); + } + } + } diff --git a/projects/SelfTest/UsageTests/Generators.tests.cpp b/projects/SelfTest/UsageTests/Generators.tests.cpp index b2a0c4e1..0110b442 100644 --- a/projects/SelfTest/UsageTests/Generators.tests.cpp +++ b/projects/SelfTest/UsageTests/Generators.tests.cpp @@ -56,24 +56,7 @@ TEST_CASE("tables", "[generators]") { #ifdef __cpp_structured_bindings -// One way to do pairs of values (actual/ expected?) -// For a simple case like this I'd recommend writing out a series of REQUIREs -// but it demonstrates a possible usage. -// Spelling out the pair like this is a bit verbose, so read on for better examples -// - the use of structured bindings here is an optional convenience -TEST_CASE( "strlen", "[approvals][generators]" ) { - auto [test_input, expected] = GENERATE( values>({ - {"one", 3}, - {"two", 3}, - {"three", 5}, - {"four", 4} - })); - - REQUIRE( test_input.size() == expected ); -} - -// A nicer way to do pairs (or more) of values - using the table generator. -// Note, you must specify the types up-front. +// Structured bindings make the table utility much nicer to use TEST_CASE( "strlen2", "[approvals][generators]" ) { auto [test_input, expected] = GENERATE( table({ {"one", 3}, @@ -129,3 +112,36 @@ SCENARIO("Eating cucumbers", "[generators][approvals]") { } } #endif + +// There are also some generic generator manipulators +TEST_CASE("Generators -- adapters", "[generators]") { + // TODO: This won't work yet, introduce GENERATE_VAR? + //auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 }); + SECTION("Filtering by predicate") { + // This filters out all odd (false) numbers, giving [2, 4, 6] + auto i = GENERATE(filter([] (int val) { return val % 2 == 0; }, values({ 1, 2, 3, 4, 5, 6 }))); + REQUIRE(i % 2 == 0); + } + SECTION("Shortening a range") { + // This takes the first 3 elements from the values, giving back [1, 2, 3] + auto i = GENERATE(take(3, values({ 1, 2, 3, 4, 5, 6 }))); + REQUIRE(i < 4); + } + SECTION("Transforming elements") { + SECTION("Same type") { + // This doubles values [1, 2, 3] into [2, 4, 6] + auto i = GENERATE(map([] (int val) { return val * 2; }, values({ 1, 2, 3 }))); + REQUIRE(i % 2 == 0); + } + SECTION("Different type") { + // This takes a generator that returns ints and maps them into strings + auto i = GENERATE(map([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 }))); + REQUIRE(i.size() == 1); + } + } + SECTION("Repeating a generator") { + // This will return values [1, 2, 3, 1, 2, 3] + auto j = GENERATE(repeat(2, values({ 1, 2, 3 }))); + REQUIRE(j > 0); + } +}