From 44c597f0742b491399994afa00070b99d3ef2885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Mon, 12 Jan 2026 14:10:25 +0100 Subject: [PATCH] Add Concat generator for combining multiple generators --- docs/generators.md | 40 +++- .../generators/catch_generators_adapters.hpp | 43 ++++ .../Baselines/automake.sw.approved.txt | 2 + .../Baselines/automake.sw.multi.approved.txt | 2 + .../Baselines/compact.sw.approved.txt | 24 ++- .../Baselines/compact.sw.multi.approved.txt | 24 ++- .../Baselines/console.std.approved.txt | 4 +- .../Baselines/console.sw.approved.txt | 174 ++++++++++++++- .../Baselines/console.sw.multi.approved.txt | 174 ++++++++++++++- .../SelfTest/Baselines/junit.sw.approved.txt | 8 +- .../Baselines/junit.sw.multi.approved.txt | 8 +- .../Baselines/sonarqube.sw.approved.txt | 6 + .../Baselines/sonarqube.sw.multi.approved.txt | 6 + tests/SelfTest/Baselines/tap.sw.approved.txt | 42 +++- .../Baselines/tap.sw.multi.approved.txt | 42 +++- .../Baselines/teamcity.sw.approved.txt | 4 + .../Baselines/teamcity.sw.multi.approved.txt | 4 + tests/SelfTest/Baselines/xml.sw.approved.txt | 200 +++++++++++++++++- .../Baselines/xml.sw.multi.approved.txt | 200 +++++++++++++++++- .../GeneratorsImpl.tests.cpp | 20 +- .../SelfTest/UsageTests/Generators.tests.cpp | 13 ++ 21 files changed, 1020 insertions(+), 20 deletions(-) diff --git a/docs/generators.md b/docs/generators.md index 27bedacc..b01ec906 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -1,6 +1,12 @@ # Data Generators +**Contents**
+[Combining `GENERATE` and `SECTION`.](#combining-generate-and-section)
+[Provided generators](#provided-generators)
+[Generator interface](#generator-interface)
+[Other usage examples](#other-usage-examples)
+ > Introduced in Catch2 2.6.0. Data generators (also known as _data driven/parametrized test cases_) @@ -106,7 +112,7 @@ a test case, * 2 fundamental generators * `SingleValueGenerator` -- contains only single element * `FixedValuesGenerator` -- contains multiple elements -* 5 generic generators that modify other generators (defined in `catch2/generators/catch_generators_adapters.hpp`) +* 6 generic generators that modify other generators (defined in `catch2/generators/catch_generators_adapters.hpp`) * `FilterGenerator` -- filters out elements from a generator for which the predicate returns "false" * `TakeGenerator` -- takes first `n` elements from a generator @@ -114,6 +120,7 @@ a test case, * `MapGenerator` -- returns the result of applying `Func` on elements from a different generator * `ChunkGenerator` -- returns chunks (inside `std::vector`) of n elements from a generator + * `ConcatGenerator` -- returns elements from multiple generators as if they were one * 2 random generators (defined in `catch2/generators/catch_generators_random.hpp`) * `RandomIntegerGenerator` -- generates random Integrals from range * `RandomFloatGenerator` -- generates random Floats from range @@ -125,6 +132,8 @@ a test case, > `IteratorGenerator` was introduced in Catch2 2.10.0. +> `ConcatGenerator` was introduced in Catch2 X.Y.Z + The generators also have associated helper functions that infer their type, making their usage much nicer. These are @@ -142,6 +151,7 @@ type, making their usage much nicer. These are * `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator` with a custom step size * `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator` * `from_range(Container const&)` for `IteratorGenerator` +* `cat(GeneratorWrapper&&...)` for `ConcatGenerator` > `chunk()`, `random()` and both `range()` functions were introduced in Catch2 2.7.0. @@ -149,6 +159,8 @@ type, making their usage much nicer. These are > `range()` for floating point numbers has been introduced in Catch2 2.11.0 +> `cat` has been introduced in Catch2 X.Y.Z + And can be used as shown in the example below to create a generator that returns 100 odd random number: @@ -278,6 +290,32 @@ to be an error or not. * If empty generator **is not** an error, use the [`SKIP` macro](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor. +## Other usage examples + +### Adding a reproducer to random tests + +If you use generators to generate random inputs for testing, you might +want to combine them with specific inputs, e.g. reproducers for previously +found issues. + +Because `GENERATE` accepts multiple values/generators, the basic case is simple: +```cpp +const int input = GENERATE(1, 2, take(10, random(10, 10'000'000))); +``` +This will set `input` first to "1", then to "2", and then to 10 random +integers. + +But if you process the random inputs further (e.g. via `map`), you can't +rely on `GENERATE`'s support for multiple generators. In that case, you +have to use the `cat` generator combinator. +```cpp +const auto input = GENERATE( + map( foo, + cat( value( 4 ), take( 10, random( 10, 10'000'000 ) ) ) ) ); +``` +This will set `input` first to `foo(4)`, before transforming the 10 random +integers through `foo`. + --- diff --git a/src/catch2/generators/catch_generators_adapters.hpp b/src/catch2/generators/catch_generators_adapters.hpp index e5ddc25e..01f7c622 100644 --- a/src/catch2/generators/catch_generators_adapters.hpp +++ b/src/catch2/generators/catch_generators_adapters.hpp @@ -234,6 +234,49 @@ namespace Generators { ); } + template + class ConcatGenerator final : public IGenerator { + std::vector> m_generators; + size_t m_current_generator = 0; + + void InsertGenerators( GeneratorWrapper&& gen ) { + m_generators.push_back( CATCH_MOVE( gen ) ); + } + + template + void InsertGenerators( GeneratorWrapper&& gen, Generators&&... gens ) { + m_generators.push_back( CATCH_MOVE( gen ) ); + InsertGenerators( CATCH_MOVE( gens )... ); + } + + public: + template + ConcatGenerator( Generators&&... generators ) { + InsertGenerators( CATCH_MOVE( generators )... ); + } + + T const& get() const override { + return m_generators[m_current_generator].get(); + } + bool next() override { + const bool success = m_generators[m_current_generator].next(); + if ( success ) { return true; } + + // If current generator is used up, we have to move to the next one + ++m_current_generator; + return m_current_generator < m_generators.size(); + } + }; + + template + GeneratorWrapper cat( GeneratorWrapper&& generator, + Generators&&... generators ) { + return GeneratorWrapper( + Catch::Detail::make_unique>( + CATCH_MOVE( generator ), CATCH_MOVE( generators )... ) ); + } + + } // namespace Generators } // namespace Catch diff --git a/tests/SelfTest/Baselines/automake.sw.approved.txt b/tests/SelfTest/Baselines/automake.sw.approved.txt index 05c6384b..606d7ac3 100644 --- a/tests/SelfTest/Baselines/automake.sw.approved.txt +++ b/tests/SelfTest/Baselines/automake.sw.approved.txt @@ -131,6 +131,7 @@ Nor would this :test-result: PASS Comparisons with int literals don't warn when mixing signed/ unsigned :test-result: PASS Composed generic matchers shortcircuit :test-result: PASS Composed matchers shortcircuit +:test-result: PASS ConcatGenerator :test-result: FAIL Contains string matcher :test-result: PASS Copy and then generate a range :test-result: PASS Cout stream properly declares it writes to stdout @@ -334,6 +335,7 @@ Message from section two :test-result: PASS array -> toString :test-result: PASS benchmark function call :test-result: PASS boolean member +:test-result: PASS cat generator :test-result: PASS checkedElse :test-result: FAIL checkedElse, failing :test-result: PASS checkedIf diff --git a/tests/SelfTest/Baselines/automake.sw.multi.approved.txt b/tests/SelfTest/Baselines/automake.sw.multi.approved.txt index 75c116d5..fd146fe7 100644 --- a/tests/SelfTest/Baselines/automake.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/automake.sw.multi.approved.txt @@ -129,6 +129,7 @@ :test-result: PASS Comparisons with int literals don't warn when mixing signed/ unsigned :test-result: PASS Composed generic matchers shortcircuit :test-result: PASS Composed matchers shortcircuit +:test-result: PASS ConcatGenerator :test-result: FAIL Contains string matcher :test-result: PASS Copy and then generate a range :test-result: PASS Cout stream properly declares it writes to stdout @@ -327,6 +328,7 @@ :test-result: PASS array -> toString :test-result: PASS benchmark function call :test-result: PASS boolean member +:test-result: PASS cat generator :test-result: PASS checkedElse :test-result: FAIL checkedElse, failing :test-result: PASS checkedIf diff --git a/tests/SelfTest/Baselines/compact.sw.approved.txt b/tests/SelfTest/Baselines/compact.sw.approved.txt index ea8d519a..a366d006 100644 --- a/tests/SelfTest/Baselines/compact.sw.approved.txt +++ b/tests/SelfTest/Baselines/compact.sw.approved.txt @@ -540,6 +540,18 @@ Matchers.tests.cpp:: passed: !second.matchCalled for: true Matchers.tests.cpp:: passed: matcher.match( 1 ) for: true Matchers.tests.cpp:: passed: first.matchCalled for: true Matchers.tests.cpp:: passed: !second.matchCalled for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: !(c.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 2 == 2 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 3 == 3 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 4 == 4 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == 5 for: 5 == 5 +GeneratorsImpl.tests.cpp:: passed: !(c.next()) for: !false Matchers.tests.cpp:: failed: testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive) Matchers.tests.cpp:: failed: testStringForMatching(), ContainsSubstring( "STRING" ) for: "this string contains 'abc' as a substring" contains: "STRING" Generators.tests.cpp:: passed: elem % 2 == 1 for: 1 == 1 @@ -2483,6 +2495,14 @@ InternalBenchmark.tests.cpp:: passed: model.started == 0 for: 0 == InternalBenchmark.tests.cpp:: passed: model.finished == 0 for: 0 == 0 InternalBenchmark.tests.cpp:: passed: called == 1 for: 1 == 1 Tricky.tests.cpp:: passed: obj.prop != 0 for: 0x != 0 +Generators.tests.cpp:: passed: input < 3 for: 0 < 3 +Generators.tests.cpp:: passed: input < 3 for: 1 < 3 +Generators.tests.cpp:: passed: input < 3 for: 2 < 3 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 Misc.tests.cpp:: passed: flag for: true Misc.tests.cpp:: passed: testCheckedElse( true ) for: true Misc.tests.cpp:: failed - but was ok: flag for: false @@ -2895,7 +2915,7 @@ InternalBenchmark.tests.cpp:: passed: med == 18. for: 18.0 == 18.0 InternalBenchmark.tests.cpp:: passed: q3 == 23. for: 23.0 == 23.0 Misc.tests.cpp:: passed: Misc.tests.cpp:: passed: -test cases: 437 | 317 passed | 96 failed | 6 skipped | 18 failed as expected -assertions: 2311 | 2110 passed | 158 failed | 43 failed as expected +test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected +assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected diff --git a/tests/SelfTest/Baselines/compact.sw.multi.approved.txt b/tests/SelfTest/Baselines/compact.sw.multi.approved.txt index dbfab609..dab12a55 100644 --- a/tests/SelfTest/Baselines/compact.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/compact.sw.multi.approved.txt @@ -538,6 +538,18 @@ Matchers.tests.cpp:: passed: !second.matchCalled for: true Matchers.tests.cpp:: passed: matcher.match( 1 ) for: true Matchers.tests.cpp:: passed: first.matchCalled for: true Matchers.tests.cpp:: passed: !second.matchCalled for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: !(c.next()) for: !false +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 1 == 1 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 2 == 2 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 3 == 3 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == i + 1 for: 4 == 4 +GeneratorsImpl.tests.cpp:: passed: c.next() for: true +GeneratorsImpl.tests.cpp:: passed: c.get() == 5 for: 5 == 5 +GeneratorsImpl.tests.cpp:: passed: !(c.next()) for: !false Matchers.tests.cpp:: failed: testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive) Matchers.tests.cpp:: failed: testStringForMatching(), ContainsSubstring( "STRING" ) for: "this string contains 'abc' as a substring" contains: "STRING" Generators.tests.cpp:: passed: elem % 2 == 1 for: 1 == 1 @@ -2476,6 +2488,14 @@ InternalBenchmark.tests.cpp:: passed: model.started == 0 for: 0 == InternalBenchmark.tests.cpp:: passed: model.finished == 0 for: 0 == 0 InternalBenchmark.tests.cpp:: passed: called == 1 for: 1 == 1 Tricky.tests.cpp:: passed: obj.prop != 0 for: 0x != 0 +Generators.tests.cpp:: passed: input < 3 for: 0 < 3 +Generators.tests.cpp:: passed: input < 3 for: 1 < 3 +Generators.tests.cpp:: passed: input < 3 for: 2 < 3 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 +Generators.tests.cpp:: passed: input % 2 == 0 for: 0 == 0 Misc.tests.cpp:: passed: flag for: true Misc.tests.cpp:: passed: testCheckedElse( true ) for: true Misc.tests.cpp:: failed - but was ok: flag for: false @@ -2884,7 +2904,7 @@ InternalBenchmark.tests.cpp:: passed: med == 18. for: 18.0 == 18.0 InternalBenchmark.tests.cpp:: passed: q3 == 23. for: 23.0 == 23.0 Misc.tests.cpp:: passed: Misc.tests.cpp:: passed: -test cases: 437 | 317 passed | 96 failed | 6 skipped | 18 failed as expected -assertions: 2311 | 2110 passed | 158 failed | 43 failed as expected +test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected +assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected diff --git a/tests/SelfTest/Baselines/console.std.approved.txt b/tests/SelfTest/Baselines/console.std.approved.txt index 7de7b1f9..48bcec18 100644 --- a/tests/SelfTest/Baselines/console.std.approved.txt +++ b/tests/SelfTest/Baselines/console.std.approved.txt @@ -1743,6 +1743,6 @@ due to unexpected exception with message: Why would you throw a std::string? =============================================================================== -test cases: 437 | 335 passed | 76 failed | 7 skipped | 19 failed as expected -assertions: 2289 | 2110 passed | 136 failed | 43 failed as expected +test cases: 439 | 337 passed | 76 failed | 7 skipped | 19 failed as expected +assertions: 2309 | 2130 passed | 136 failed | 43 failed as expected diff --git a/tests/SelfTest/Baselines/console.sw.approved.txt b/tests/SelfTest/Baselines/console.sw.approved.txt index ab18d08c..eb9f7367 100644 --- a/tests/SelfTest/Baselines/console.sw.approved.txt +++ b/tests/SelfTest/Baselines/console.sw.approved.txt @@ -3911,6 +3911,80 @@ Matchers.tests.cpp:: PASSED: with expansion: true +------------------------------------------------------------------------------- +ConcatGenerator + Cat support single-generator construction +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( c.next() ) +with expansion: + !false + +------------------------------------------------------------------------------- +ConcatGenerator + Iterating over multiple generators +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 2 == 2 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 3 == 3 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 4 == 4 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == 5 ) +with expansion: + 5 == 5 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( c.next() ) +with expansion: + !false + ------------------------------------------------------------------------------- Contains string matcher ------------------------------------------------------------------------------- @@ -16440,6 +16514,102 @@ Tricky.tests.cpp:: PASSED: with expansion: 0x != 0 +------------------------------------------------------------------------------- +cat generator + Simple usage +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input < 3 ) +with expansion: + 0 < 3 + +------------------------------------------------------------------------------- +cat generator + Simple usage +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input < 3 ) +with expansion: + 1 < 3 + +------------------------------------------------------------------------------- +cat generator + Simple usage +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input < 3 ) +with expansion: + 2 < 3 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- checkedElse ------------------------------------------------------------------------------- @@ -19372,6 +19542,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 437 | 317 passed | 96 failed | 6 skipped | 18 failed as expected -assertions: 2311 | 2110 passed | 158 failed | 43 failed as expected +test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected +assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected diff --git a/tests/SelfTest/Baselines/console.sw.multi.approved.txt b/tests/SelfTest/Baselines/console.sw.multi.approved.txt index 05d6eb6c..4d0bd8f4 100644 --- a/tests/SelfTest/Baselines/console.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/console.sw.multi.approved.txt @@ -3909,6 +3909,80 @@ Matchers.tests.cpp:: PASSED: with expansion: true +------------------------------------------------------------------------------- +ConcatGenerator + Cat support single-generator construction +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( c.next() ) +with expansion: + !false + +------------------------------------------------------------------------------- +ConcatGenerator + Iterating over multiple generators +------------------------------------------------------------------------------- +GeneratorsImpl.tests.cpp: +............................................................................... + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 1 == 1 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 2 == 2 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 3 == 3 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == i + 1 ) +with expansion: + 4 == 4 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.next() ) +with expansion: + true + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE( c.get() == 5 ) +with expansion: + 5 == 5 + +GeneratorsImpl.tests.cpp:: PASSED: + REQUIRE_FALSE( c.next() ) +with expansion: + !false + ------------------------------------------------------------------------------- Contains string matcher ------------------------------------------------------------------------------- @@ -16433,6 +16507,102 @@ Tricky.tests.cpp:: PASSED: with expansion: 0x != 0 +------------------------------------------------------------------------------- +cat generator + Simple usage +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input < 3 ) +with expansion: + 0 < 3 + +------------------------------------------------------------------------------- +cat generator + Simple usage +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input < 3 ) +with expansion: + 1 < 3 + +------------------------------------------------------------------------------- +cat generator + Simple usage +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input < 3 ) +with expansion: + 2 < 3 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + +------------------------------------------------------------------------------- +cat generator + Used in map +------------------------------------------------------------------------------- +Generators.tests.cpp: +............................................................................... + +Generators.tests.cpp:: PASSED: + REQUIRE( input % 2 == 0 ) +with expansion: + 0 == 0 + ------------------------------------------------------------------------------- checkedElse ------------------------------------------------------------------------------- @@ -19361,6 +19531,6 @@ Misc.tests.cpp: Misc.tests.cpp:: PASSED: =============================================================================== -test cases: 437 | 317 passed | 96 failed | 6 skipped | 18 failed as expected -assertions: 2311 | 2110 passed | 158 failed | 43 failed as expected +test cases: 439 | 319 passed | 96 failed | 6 skipped | 18 failed as expected +assertions: 2331 | 2130 passed | 158 failed | 43 failed as expected diff --git a/tests/SelfTest/Baselines/junit.sw.approved.txt b/tests/SelfTest/Baselines/junit.sw.approved.txt index 10523416..d08e4f81 100644 --- a/tests/SelfTest/Baselines/junit.sw.approved.txt +++ b/tests/SelfTest/Baselines/junit.sw.approved.txt @@ -1,7 +1,7 @@ - + @@ -501,6 +501,9 @@ at Message.tests.cpp: + + + FAILED: @@ -1993,6 +1996,9 @@ at Skip.tests.cpp: + + + diff --git a/tests/SelfTest/Baselines/junit.sw.multi.approved.txt b/tests/SelfTest/Baselines/junit.sw.multi.approved.txt index 2199b7f7..5d7dce1d 100644 --- a/tests/SelfTest/Baselines/junit.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/junit.sw.multi.approved.txt @@ -1,6 +1,6 @@ - + @@ -500,6 +500,9 @@ at Message.tests.cpp: + + + FAILED: @@ -1992,6 +1995,9 @@ at Skip.tests.cpp: + + + diff --git a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt index 27dc2cdd..2c5d4a90 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.approved.txt +++ b/tests/SelfTest/Baselines/sonarqube.sw.approved.txt @@ -145,6 +145,9 @@ at AssertionHandler.tests.cpp: + + + @@ -1242,6 +1245,9 @@ at Generators.tests.cpp: + + + diff --git a/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt b/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt index 93e3bcc8..9baa1c15 100644 --- a/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/sonarqube.sw.multi.approved.txt @@ -144,6 +144,9 @@ at AssertionHandler.tests.cpp: + + + @@ -1241,6 +1244,9 @@ at Generators.tests.cpp: + + + diff --git a/tests/SelfTest/Baselines/tap.sw.approved.txt b/tests/SelfTest/Baselines/tap.sw.approved.txt index 0b5e1c91..5804c06a 100644 --- a/tests/SelfTest/Baselines/tap.sw.approved.txt +++ b/tests/SelfTest/Baselines/tap.sw.approved.txt @@ -980,6 +980,30 @@ ok {test-number} - matcher.match( 1 ) for: true ok {test-number} - first.matchCalled for: true # Composed matchers shortcircuit ok {test-number} - !second.matchCalled for: true +# ConcatGenerator +ok {test-number} - c.get() == 1 for: 1 == 1 +# ConcatGenerator +ok {test-number} - !(c.next()) for: !false +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 1 == 1 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 2 == 2 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 3 == 3 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 4 == 4 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == 5 for: 5 == 5 +# ConcatGenerator +ok {test-number} - !(c.next()) for: !false # Contains string matcher not ok {test-number} - testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive) # Contains string matcher @@ -3933,6 +3957,22 @@ ok {test-number} - model.finished == 0 for: 0 == 0 ok {test-number} - called == 1 for: 1 == 1 # boolean member ok {test-number} - obj.prop != 0 for: 0x != 0 +# cat generator +ok {test-number} - input < 3 for: 0 < 3 +# cat generator +ok {test-number} - input < 3 for: 1 < 3 +# cat generator +ok {test-number} - input < 3 for: 2 < 3 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 # checkedElse ok {test-number} - flag for: true # checkedElse @@ -4641,5 +4681,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..2323 +1..2343 diff --git a/tests/SelfTest/Baselines/tap.sw.multi.approved.txt b/tests/SelfTest/Baselines/tap.sw.multi.approved.txt index de5c68fb..85e5a226 100644 --- a/tests/SelfTest/Baselines/tap.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/tap.sw.multi.approved.txt @@ -978,6 +978,30 @@ ok {test-number} - matcher.match( 1 ) for: true ok {test-number} - first.matchCalled for: true # Composed matchers shortcircuit ok {test-number} - !second.matchCalled for: true +# ConcatGenerator +ok {test-number} - c.get() == 1 for: 1 == 1 +# ConcatGenerator +ok {test-number} - !(c.next()) for: !false +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 1 == 1 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 2 == 2 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 3 == 3 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == i + 1 for: 4 == 4 +# ConcatGenerator +ok {test-number} - c.next() for: true +# ConcatGenerator +ok {test-number} - c.get() == 5 for: 5 == 5 +# ConcatGenerator +ok {test-number} - !(c.next()) for: !false # Contains string matcher not ok {test-number} - testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive) # Contains string matcher @@ -3926,6 +3950,22 @@ ok {test-number} - model.finished == 0 for: 0 == 0 ok {test-number} - called == 1 for: 1 == 1 # boolean member ok {test-number} - obj.prop != 0 for: 0x != 0 +# cat generator +ok {test-number} - input < 3 for: 0 < 3 +# cat generator +ok {test-number} - input < 3 for: 1 < 3 +# cat generator +ok {test-number} - input < 3 for: 2 < 3 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 +# cat generator +ok {test-number} - input % 2 == 0 for: 0 == 0 # checkedElse ok {test-number} - flag for: true # checkedElse @@ -4630,5 +4670,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 ok {test-number} - # xmlentitycheck ok {test-number} - -1..2323 +1..2343 diff --git a/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/tests/SelfTest/Baselines/teamcity.sw.approved.txt index 45e97905..6705806c 100644 --- a/tests/SelfTest/Baselines/teamcity.sw.approved.txt +++ b/tests/SelfTest/Baselines/teamcity.sw.approved.txt @@ -303,6 +303,8 @@ ##teamcity[testFinished name='Composed generic matchers shortcircuit' duration="{duration}"] ##teamcity[testStarted name='Composed matchers shortcircuit'] ##teamcity[testFinished name='Composed matchers shortcircuit' duration="{duration}"] +##teamcity[testStarted name='ConcatGenerator'] +##teamcity[testFinished name='ConcatGenerator' duration="{duration}"] ##teamcity[testStarted name='Contains string matcher'] ##teamcity[testFailed name='Contains string matcher' message='Matchers.tests.cpp:|n...............................................................................|n|nMatchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) )|nwith expansion:|n "this string contains |'abc|' as a substring" contains: "not there" (case insensitive)|n'] ##teamcity[testFailed name='Contains string matcher' message='Matchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), ContainsSubstring( "STRING" ) )|nwith expansion:|n "this string contains |'abc|' as a substring" contains: "STRING"|n'] @@ -818,6 +820,8 @@ ##teamcity[testFinished name='benchmark function call' duration="{duration}"] ##teamcity[testStarted name='boolean member'] ##teamcity[testFinished name='boolean member' duration="{duration}"] +##teamcity[testStarted name='cat generator'] +##teamcity[testFinished name='cat generator' duration="{duration}"] ##teamcity[testStarted name='checkedElse'] ##teamcity[testFinished name='checkedElse' duration="{duration}"] ##teamcity[testStarted name='checkedElse, failing'] diff --git a/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt b/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt index 787bd222..80d53df2 100644 --- a/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/teamcity.sw.multi.approved.txt @@ -303,6 +303,8 @@ ##teamcity[testFinished name='Composed generic matchers shortcircuit' duration="{duration}"] ##teamcity[testStarted name='Composed matchers shortcircuit'] ##teamcity[testFinished name='Composed matchers shortcircuit' duration="{duration}"] +##teamcity[testStarted name='ConcatGenerator'] +##teamcity[testFinished name='ConcatGenerator' duration="{duration}"] ##teamcity[testStarted name='Contains string matcher'] ##teamcity[testFailed name='Contains string matcher' message='Matchers.tests.cpp:|n...............................................................................|n|nMatchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), ContainsSubstring( "not there", Catch::CaseSensitive::No ) )|nwith expansion:|n "this string contains |'abc|' as a substring" contains: "not there" (case insensitive)|n'] ##teamcity[testFailed name='Contains string matcher' message='Matchers.tests.cpp:|nexpression failed|n CHECK_THAT( testStringForMatching(), ContainsSubstring( "STRING" ) )|nwith expansion:|n "this string contains |'abc|' as a substring" contains: "STRING"|n'] @@ -818,6 +820,8 @@ ##teamcity[testFinished name='benchmark function call' duration="{duration}"] ##teamcity[testStarted name='boolean member'] ##teamcity[testFinished name='boolean member' duration="{duration}"] +##teamcity[testStarted name='cat generator'] +##teamcity[testFinished name='cat generator' duration="{duration}"] ##teamcity[testStarted name='checkedElse'] ##teamcity[testFinished name='checkedElse' duration="{duration}"] ##teamcity[testStarted name='checkedElse, failing'] diff --git a/tests/SelfTest/Baselines/xml.sw.approved.txt b/tests/SelfTest/Baselines/xml.sw.approved.txt index 95a7cb2f..b719f1a0 100644 --- a/tests/SelfTest/Baselines/xml.sw.approved.txt +++ b/tests/SelfTest/Baselines/xml.sw.approved.txt @@ -4376,6 +4376,111 @@ C + +
+ + + c.get() == 1 + + + 1 == 1 + + + + + !(c.next()) + + + !false + + + +
+
+ + + c.get() == i + 1 + + + 1 == 1 + + + + + c.next() + + + true + + + + + c.get() == i + 1 + + + 2 == 2 + + + + + c.next() + + + true + + + + + c.get() == i + 1 + + + 3 == 3 + + + + + c.next() + + + true + + + + + c.get() == i + 1 + + + 4 == 4 + + + + + c.next() + + + true + + + + + c.get() == 5 + + + 5 == 5 + + + + + !(c.next()) + + + !false + + + +
+ +
@@ -19155,6 +19260,97 @@ Approx( 1.23999999999999999 ) + +
+ + + input < 3 + + + 0 < 3 + + + +
+
+ + + input < 3 + + + 1 < 3 + + + +
+
+ + + input < 3 + + + 2 < 3 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+ +
@@ -22416,6 +22612,6 @@ Approx( -1.95996398454005449 ) - - + + diff --git a/tests/SelfTest/Baselines/xml.sw.multi.approved.txt b/tests/SelfTest/Baselines/xml.sw.multi.approved.txt index 68ae54fe..051f9a82 100644 --- a/tests/SelfTest/Baselines/xml.sw.multi.approved.txt +++ b/tests/SelfTest/Baselines/xml.sw.multi.approved.txt @@ -4376,6 +4376,111 @@ C
+ +
+ + + c.get() == 1 + + + 1 == 1 + + + + + !(c.next()) + + + !false + + + +
+
+ + + c.get() == i + 1 + + + 1 == 1 + + + + + c.next() + + + true + + + + + c.get() == i + 1 + + + 2 == 2 + + + + + c.next() + + + true + + + + + c.get() == i + 1 + + + 3 == 3 + + + + + c.next() + + + true + + + + + c.get() == i + 1 + + + 4 == 4 + + + + + c.next() + + + true + + + + + c.get() == 5 + + + 5 == 5 + + + + + !(c.next()) + + + !false + + + +
+ +
@@ -19155,6 +19260,97 @@ Approx( 1.23999999999999999 ) + +
+ + + input < 3 + + + 0 < 3 + + + +
+
+ + + input < 3 + + + 1 < 3 + + + +
+
+ + + input < 3 + + + 2 < 3 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+
+ + + input % 2 == 0 + + + 0 == 0 + + + +
+ +
@@ -22415,6 +22611,6 @@ Approx( -1.95996398454005449 ) - - + + diff --git a/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp b/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp index c044547d..0ba4ab43 100644 --- a/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp +++ b/tests/SelfTest/IntrospectiveTests/GeneratorsImpl.tests.cpp @@ -85,7 +85,7 @@ TEST_CASE("Generators internals", "[generators][internals]") { filter([](int) { return false; }, values({ 1, 2, 3 })), Catch::GeneratorException); } - + // Non-trivial usage SECTION("Out-of-line predicates are copied into the generator") { auto evilNumber = Catch::Detail::make_unique(2); @@ -586,3 +586,21 @@ TEST_CASE("from_range(container) supports ADL begin/end and arrays", "[generator } } + +TEST_CASE( "ConcatGenerator", "[generators][concat]" ) { + using namespace Catch::Generators; + SECTION( "Cat support single-generator construction" ) { + ConcatGenerator c( value( 1 ) ); + REQUIRE( c.get() == 1 ); + REQUIRE_FALSE( c.next() ); + } + SECTION( "Iterating over multiple generators" ) { + ConcatGenerator c( value( 1 ), values( { 2, 3, 4 } ), value( 5 ) ); + for ( int i = 0; i < 4; ++i ) { + REQUIRE( c.get() == i + 1 ); + REQUIRE( c.next() ); + } + REQUIRE( c.get() == 5 ); + REQUIRE_FALSE( c.next() ); + } +} diff --git a/tests/SelfTest/UsageTests/Generators.tests.cpp b/tests/SelfTest/UsageTests/Generators.tests.cpp index f04cf4f0..b03192cf 100644 --- a/tests/SelfTest/UsageTests/Generators.tests.cpp +++ b/tests/SelfTest/UsageTests/Generators.tests.cpp @@ -321,3 +321,16 @@ TEST_CASE( "GENERATE can combine literals and generators", "[generators]" ) { random( -100, 100 ) ) ) ); REQUIRE( i % 2 == 0 ); } + +TEST_CASE( "cat generator", "[generators][concat]" ) { + SECTION("Simple usage") { + const int input = GENERATE( cat( value( 0 ) ), cat( value( 1 ), value( 2 ) ) ); + REQUIRE( input < 3 ); + } + SECTION( "Used in map" ) { + const int input = GENERATE( + map( []( int i ) { return i * 2; }, + cat( value( 1 ), take( 4, random( 10, 10'000'000 ) ) ) ) ); + REQUIRE( input % 2 == 0 ); + } +}