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 );
+ }
+}