// Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 #if defined( __GNUC__ ) || defined( __clang__ ) # pragma GCC diagnostic ignored "-Wfloat-equal" #endif #include #include #include #include #include #include #include #include // Tests of generator implementation details TEST_CASE("Generators internals", "[generators][internals]") { using namespace Catch::Generators; SECTION("Single value") { auto gen = value(123); REQUIRE(gen.get() == 123); REQUIRE_FALSE(gen.next()); } SECTION("Preset values") { auto gen = values({ 1, 3, 5 }); REQUIRE(gen.get() == 1); REQUIRE(gen.next()); REQUIRE(gen.get() == 3); REQUIRE(gen.next()); REQUIRE(gen.get() == 5); REQUIRE_FALSE(gen.next()); } SECTION("Generator combinator") { auto gen = makeGenerators(1, 5, values({ 2, 4 }), 0); REQUIRE(gen.get() == 1); REQUIRE(gen.next()); REQUIRE(gen.get() == 5); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE(gen.next()); REQUIRE(gen.get() == 4); REQUIRE(gen.next()); REQUIRE(gen.get() == 0); REQUIRE_FALSE(gen.next()); } SECTION("Explicitly typed generator sequence") { auto gen = makeGenerators(as{}, "aa", "bb", "cc"); // This just checks that the type is std::string: REQUIRE(gen.get().size() == 2); // Iterate over the generator REQUIRE(gen.get() == "aa"); REQUIRE(gen.next()); REQUIRE(gen.get() == "bb"); REQUIRE(gen.next()); REQUIRE(gen.get() == "cc"); REQUIRE_FALSE(gen.next()); } SECTION("Filter generator") { // Normal usage SECTION("Simple filtering") { 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()); } SECTION("Filter out multiple elements at the start and end") { auto gen = filter([](int i) { return i != 2; }, values({ 2, 2, 1, 3, 2, 2 })); REQUIRE(gen.get() == 1); REQUIRE(gen.next()); REQUIRE(gen.get() == 3); REQUIRE_FALSE(gen.next()); } SECTION("Throws on construction if it can't get initial element") { REQUIRE_THROWS_AS(filter([](int) { return false; }, value(1)), Catch::GeneratorException); REQUIRE_THROWS_AS( 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); auto gen = [&]{ const auto predicate = [&](int i) { return i != *evilNumber; }; return filter(predicate, values({ 2, 1, 2, 3, 2, 2 })); }(); REQUIRE(gen.get() == 1); REQUIRE(gen.next()); REQUIRE(gen.get() == 3); REQUIRE_FALSE(gen.next()); } } 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 with explicit return type") { 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("Map with deduced return type") { 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()); } } SECTION("Range") { SECTION("Positive auto step") { SECTION("Integer") { auto gen = range(-2, 2); REQUIRE(gen.get() == -2); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == 0); REQUIRE(gen.next()); REQUIRE(gen.get() == 1); REQUIRE_FALSE(gen.next()); } } SECTION("Negative auto step") { SECTION("Integer") { auto gen = range(2, -2); REQUIRE(gen.get() == 2); REQUIRE(gen.next()); REQUIRE(gen.get() == 1); REQUIRE(gen.next()); REQUIRE(gen.get() == 0); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE_FALSE(gen.next()); } } SECTION("Positive manual step") { SECTION("Integer") { SECTION("Exact") { auto gen = range(-7, 5, 3); REQUIRE(gen.get() == -7); REQUIRE(gen.next()); REQUIRE(gen.get() == -4); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE_FALSE(gen.next()); } SECTION("Slightly over end") { auto gen = range(-7, 4, 3); REQUIRE(gen.get() == -7); REQUIRE(gen.next()); REQUIRE(gen.get() == -4); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE_FALSE(gen.next()); } SECTION("Slightly under end") { auto gen = range(-7, 6, 3); REQUIRE(gen.get() == -7); REQUIRE(gen.next()); REQUIRE(gen.get() == -4); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE(gen.next()); REQUIRE(gen.get() == 5); REQUIRE_FALSE(gen.next()); } } SECTION("Floating Point") { using Catch::Approx; SECTION("Exact") { const auto rangeStart = -1.; const auto rangeEnd = 1.; const auto step = .1; auto gen = range(rangeStart, rangeEnd, step); auto expected = rangeStart; while( (rangeEnd - expected) > step ) { INFO( "Current expected value is " << expected ); REQUIRE(gen.get() == Approx(expected)); REQUIRE(gen.next()); expected += step; } REQUIRE(gen.get() == Approx( rangeEnd ) ); REQUIRE_FALSE(gen.next()); } SECTION("Slightly over end") { const auto rangeStart = -1.; const auto rangeEnd = 1.; const auto step = .3; auto gen = range(rangeStart, rangeEnd, step); auto expected = rangeStart; while( (rangeEnd - expected) > step ) { INFO( "Current expected value is " << expected ); REQUIRE(gen.get() == Approx(expected)); REQUIRE(gen.next()); expected += step; } REQUIRE_FALSE(gen.next()); } SECTION("Slightly under end") { const auto rangeStart = -1.; const auto rangeEnd = .9; const auto step = .3; auto gen = range(rangeStart, rangeEnd, step); auto expected = rangeStart; while( (rangeEnd - expected) > step ) { INFO( "Current expected value is " << expected ); REQUIRE(gen.get() == Approx(expected)); REQUIRE(gen.next()); expected += step; } REQUIRE_FALSE(gen.next()); } } } SECTION("Negative manual step") { SECTION("Integer") { SECTION("Exact") { auto gen = range(5, -7, -3); REQUIRE(gen.get() == 5); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == -4); REQUIRE_FALSE(gen.next()); } SECTION("Slightly over end") { auto gen = range(5, -6, -3); REQUIRE(gen.get() == 5); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == -4); REQUIRE_FALSE(gen.next()); } SECTION("Slightly under end") { auto gen = range(5, -8, -3); REQUIRE(gen.get() == 5); REQUIRE(gen.next()); REQUIRE(gen.get() == 2); REQUIRE(gen.next()); REQUIRE(gen.get() == -1); REQUIRE(gen.next()); REQUIRE(gen.get() == -4); REQUIRE(gen.next()); REQUIRE(gen.get() == -7); REQUIRE_FALSE(gen.next()); } } } } } // todo: uncopyable type used in a generator // idea: uncopyable tag type for a stupid generator namespace { struct non_copyable { non_copyable() = default; non_copyable(non_copyable const&) = delete; non_copyable& operator=(non_copyable const&) = delete; int value = -1; }; // This class shows how to implement a simple generator for Catch tests class TestGen : public Catch::Generators::IGenerator { int current_number; public: TestGen(non_copyable const& nc): current_number(nc.value) {} int const& get() const override; bool next() override { return false; } bool isFinite() const override { return true; } }; // Avoids -Wweak-vtables int const& TestGen::get() const { return current_number; } } TEST_CASE("GENERATE capture macros", "[generators][internals][approvals]") { auto value = GENERATE(take(10, random(0, 10))); non_copyable nc; nc.value = value; // neither `GENERATE_COPY` nor plain `GENERATE` would compile here auto value2 = GENERATE_REF(Catch::Generators::GeneratorWrapper(Catch::Detail::make_unique(nc))); REQUIRE(value == value2); } TEST_CASE("#1809 - GENERATE_COPY and SingleValueGenerator does not compile", "[generators][compilation][approvals]") { // Verify Issue #1809 fix, only needs to compile. auto a = GENERATE_COPY(1, 2); (void)a; auto b = GENERATE_COPY(as{}, 1, 2); (void)b; int i = 1; int j = 2; auto c = GENERATE_COPY(i, j); (void)c; auto d = GENERATE_COPY(as{}, i, j); (void)d; SUCCEED(); } TEST_CASE("Multiple random generators in one test case output different values", "[generators][internals][approvals]") { SECTION("Integer") { auto random1 = Catch::Generators::random(0, 1000); auto random2 = Catch::Generators::random(0, 1000); size_t same = 0; for (size_t i = 0; i < 1000; ++i) { same += random1.get() == random2.get(); random1.next(); random2.next(); } // Because the previous low bound failed CI couple of times, // we use a very high threshold of 20% before failure is reported. REQUIRE(same < 200); } SECTION("Float") { auto random1 = Catch::Generators::random(0., 1000.); auto random2 = Catch::Generators::random(0., 1000.); size_t same = 0; for (size_t i = 0; i < 1000; ++i) { same += random1.get() == random2.get(); random1.next(); random2.next(); } // Because the previous low bound failed CI couple of times, // we use a very high threshold of 20% before failure is reported. REQUIRE(same < 200); } } TEST_CASE("#2040 - infinite compilation recursion in GENERATE with MSVC", "[generators][compilation][approvals]") { int x = 42; auto test = GENERATE_COPY(1, x, 2 * x); CHECK(test < 100); } namespace { static bool always_true(int) { return true; } static bool is_even(int n) { return n % 2 == 0; } static bool is_multiple_of_3(int n) { return n % 3 == 0; } } TEST_CASE("GENERATE handles function (pointers)", "[generators][compilation][approvals]") { auto f = GENERATE(always_true, is_even, is_multiple_of_3); REQUIRE(f(6)); } TEST_CASE("GENERATE decays arrays", "[generators][compilation][approvals]") { auto str = GENERATE("abc", "def", "gh"); (void)str; STATIC_REQUIRE(std::is_same::value); } TEST_CASE("Generators count returned elements", "[generators][approvals]") { auto generator = Catch::Generators::FixedValuesGenerator( { 1, 2, 3 } ); REQUIRE( generator.currentElementIndex() == 0 ); REQUIRE( generator.countedNext() ); REQUIRE( generator.currentElementIndex() == 1 ); REQUIRE( generator.countedNext() ); REQUIRE( generator.currentElementIndex() == 2 ); REQUIRE_FALSE( generator.countedNext() ); REQUIRE( generator.currentElementIndex() == 2 ); } TEST_CASE( "Generators can stringify their elements", "[generators][approvals]" ) { auto generator = Catch::Generators::FixedValuesGenerator( { 1, 2, 3 } ); REQUIRE( generator.currentElementAsString() == "1"_catch_sr ); REQUIRE( generator.countedNext() ); REQUIRE( generator.currentElementAsString() == "2"_catch_sr ); REQUIRE( generator.countedNext() ); REQUIRE( generator.currentElementAsString() == "3"_catch_sr ); } namespace { class CustomStringifyGenerator : public Catch::Generators::IGenerator { bool m_first = true; std::string stringifyImpl() const override { return m_first ? "first" : "second"; } bool next() override { if ( m_first ) { m_first = false; return true; } return false; } public: bool const& get() const override; bool isFinite() const override { return true; } }; // Avoids -Wweak-vtables bool const& CustomStringifyGenerator::get() const { return m_first; } } // namespace TEST_CASE( "Generators can override element stringification", "[generators][approvals]" ) { CustomStringifyGenerator generator; REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); REQUIRE( generator.countedNext() ); REQUIRE( generator.currentElementAsString() == "second"_catch_sr ); } namespace { class StringifyCountingGenerator : public Catch::Generators::IGenerator { bool m_first = true; mutable size_t m_stringificationCalls = 0; std::string stringifyImpl() const override { ++m_stringificationCalls; return m_first ? "first" : "second"; } bool next() override { if ( m_first ) { m_first = false; return true; } return false; } public: bool const& get() const override; size_t stringificationCalls() const { return m_stringificationCalls; } bool isFinite() const override { return true; } }; // Avoids -Wweak-vtables bool const& StringifyCountingGenerator::get() const { return m_first; } } // namespace TEST_CASE( "Generator element stringification is cached", "[generators][approvals]" ) { StringifyCountingGenerator generator; REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); REQUIRE( generator.currentElementAsString() == "first"_catch_sr ); REQUIRE( generator.stringificationCalls() == 1 ); } TEST_CASE( "Random generators can be seeded", "[generators][approvals]" ) { SECTION( "Integer generator" ) { using Catch::Generators::RandomIntegerGenerator; RandomIntegerGenerator rng1( 0, 100, 0x1234 ), rng2( 0, 100, 0x1234 ); for ( size_t i = 0; i < 10; ++i ) { REQUIRE( rng1.get() == rng2.get() ); rng1.next(); rng2.next(); } } SECTION("Float generator") { using Catch::Generators::RandomFloatingGenerator; RandomFloatingGenerator rng1( 0., 100., 0x1234 ), rng2( 0., 100., 0x1234 ); for ( size_t i = 0; i < 10; ++i ) { REQUIRE( rng1.get() == rng2.get() ); rng1.next(); rng2.next(); } } } TEST_CASE("Filter generator throws exception for empty generator", "[generators]") { using namespace Catch::Generators; REQUIRE_THROWS_AS( filter( []( int ) { return false; }, value( 3 ) ), Catch::GeneratorException ); } TEST_CASE("from_range(container) supports ADL begin/end and arrays", "[generators][from-range][approvals]") { using namespace Catch::Generators; SECTION("C array") { int arr[3]{ 5, 6, 7 }; auto gen = from_range( arr ); REQUIRE( gen.get() == 5 ); REQUIRE( gen.next() ); REQUIRE( gen.get() == 6 ); REQUIRE( gen.next() ); REQUIRE( gen.get() == 7 ); REQUIRE_FALSE( gen.next() ); } SECTION( "ADL range" ) { unrelated::needs_ADL_begin range{ 1, 2, 3 }; auto gen = from_range( range ); REQUIRE( gen.get() == 1 ); REQUIRE( gen.next() ); REQUIRE( gen.get() == 2 ); REQUIRE( gen.next() ); REQUIRE( gen.get() == 3 ); REQUIRE_FALSE( gen.next() ); } } 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() ); } } namespace { // Test the default behaviour of skipping generators forward. We do // not want to use pre-existing generator, because they will get // specialized forward skip implementation. class SkipTestGenerator : public Catch::Generators::IGenerator { std::vector m_elements{ 0, 1, 2, 3, 4, 5 }; size_t m_idx = 0; public: int const& get() const override { return m_elements[m_idx]; } bool next() override { ++m_idx; return m_idx < m_elements.size(); } bool isFinite() const override { return true; } }; } TEST_CASE( "Generators can be skipped forward", "[generators]" ) { SkipTestGenerator generator; REQUIRE( generator.currentElementIndex() == 0 ); generator.skipToNthElement( 3 ); REQUIRE( generator.currentElementIndex() == 3 ); REQUIRE( generator.get() == 3 ); // Try "skipping" to the same element. generator.skipToNthElement( 3 ); REQUIRE( generator.currentElementIndex() == 3 ); REQUIRE( generator.get() == 3 ); generator.skipToNthElement( 5 ); REQUIRE( generator.currentElementIndex() == 5 ); REQUIRE( generator.get() == 5 ); // Backwards REQUIRE_THROWS( generator.skipToNthElement( 3 ) ); // Past the end REQUIRE_THROWS( generator.skipToNthElement( 6 ) ); } TEST_CASE( "FixedValuesGenerator can be skipped forward", "[generators][values]" ) { Catch::Generators::FixedValuesGenerator values( {0, 1, 2, 3, 4} ); REQUIRE( values.currentElementIndex() == 0 ); values.skipToNthElement( 3 ); REQUIRE( values.currentElementIndex() == 3 ); REQUIRE( values.get() == 3 ); values.skipToNthElement( 4 ); REQUIRE( values.currentElementIndex() == 4 ); REQUIRE( values.get() == 4 ); // Past the end REQUIRE_THROWS( values.skipToNthElement( 5 ) ); } TEST_CASE( "TakeGenerator can be skipped forward", "[generators][take]" ) { SECTION("take is shorter than underlying") { Catch::Generators::TakeGenerator take( 6, Catch::Generators::values( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ) ); REQUIRE( take.get() == 0 ); take.skipToNthElement( 2 ); REQUIRE( take.get() == 2 ); take.skipToNthElement( 5 ); REQUIRE( take.get() == 5 ); // This is in the original values, but past the end of the take REQUIRE_THROWS( take.skipToNthElement( 6 ) ); } SECTION( "take is longer than underlying" ) { Catch::Generators::TakeGenerator take( 8, Catch::Generators::values( { 0, 1, 2, 3, 4, 5 } ) ); REQUIRE( take.get() == 0 ); take.skipToNthElement( 2 ); REQUIRE( take.get() == 2 ); take.skipToNthElement( 5 ); REQUIRE( take.get() == 5 ); // This is in the take, but outside of original values REQUIRE_THROWS( take.skipToNthElement( 6 ) ); } } TEST_CASE("MapGenerator can be skipped forward efficiently", "[generators][map]") { using namespace Catch::Generators; int map_calls = 0; auto map_func = [&map_calls]( int i ) { ++map_calls; return i; }; SECTION( "via calls to next()" ) { MapGenerator map_generator( map_func, values( { 0, 1, 2, 3, 4, 5, 6 } ) ); REQUIRE( map_calls == 0 ); map_generator.next(); map_generator.next(); map_generator.next(); REQUIRE( map_calls == 0 ); REQUIRE( map_generator.get() == 3 ); REQUIRE( map_calls == 1 ); REQUIRE( map_generator.get() == 3 ); REQUIRE( map_calls == 1 ); map_generator.next(); REQUIRE( map_calls == 1 ); } SECTION("via calls to skipToNthElement()") { MapGenerator map_generator( map_func, values( { 0, 1, 2, 3, 4, 5, 6 } ) ); REQUIRE( map_calls == 0 ); map_generator.skipToNthElement( 3 ); map_generator.skipToNthElement( 4 ); REQUIRE( map_calls == 0 ); REQUIRE( map_generator.get() == 4 ); REQUIRE( map_calls == 1 ); map_generator.skipToNthElement( 4 ); REQUIRE( map_generator.get() == 4 ); REQUIRE( map_calls == 1 ); map_generator.skipToNthElement( 6 ); REQUIRE( map_calls == 1 ); REQUIRE( map_generator.get() == 6 ); REQUIRE( map_calls == 2 ); REQUIRE_THROWS( map_generator.skipToNthElement( 7 ) ); REQUIRE( map_calls == 2 ); } } TEST_CASE( "Generator adapters properly handle isFinite", "[generators][map][take][chunk][filter][concat]" ) { using namespace Catch::Generators; SECTION( "concat generator" ) { ConcatGenerator finite_cat( value( 1 ), values( { 2, 3, 4 } ), value( 5 ) ); REQUIRE( finite_cat.isFinite() ); ConcatGenerator infinite_cat( value( 1 ), random( 1, 10 ), value( 3 ) ); REQUIRE_FALSE( infinite_cat.isFinite() ); } SECTION( "take generator" ) { TakeGenerator take_1( 2, values( { 1, 2, 3, 4, 5 } ) ); REQUIRE( take_1.isFinite() ); TakeGenerator take_2( 3, random( 1, 100 ) ); REQUIRE( take_2.isFinite() ); } SECTION( "chunk generator" ) { ChunkGenerator finite_chunk( 2, values( { 1, 2, 3, 4, 5 } ) ); REQUIRE( finite_chunk.isFinite() ); ChunkGenerator infinite_chunk( 2, random( 1, 100 ) ); REQUIRE_FALSE( infinite_chunk.isFinite() ); } SECTION( "map" ) { auto identity = []( int i ) { return i; }; MapGenerator finite_map( identity, values( { 1, 2, 3 } ) ); REQUIRE( finite_map.isFinite() ); MapGenerator infinite_map( identity, random( 1, 100 ) ); REQUIRE_FALSE( infinite_map.isFinite() ); } SECTION( "filter" ) { auto always_true = []( int ) { return true; }; FilterGenerator finite_filter( always_true, values( { 1, 2, 3, 4, 5 } ) ); REQUIRE( finite_filter.isFinite() ); FilterGenerator infinite_filter( always_true, random( 1, 100 ) ); REQUIRE_FALSE( infinite_filter.isFinite() ); } } TEST_CASE( "RepeatGenerator refuses infinite generators", "[generators][repeat]" ) { using namespace Catch::Generators; REQUIRE_THROWS( RepeatGenerator( 2, random( 1, 100 ) ) ); } TEMPLATE_TEST_CASE( "RandomGenerator reports itself as infinite", "[generators][random]", int, float, long double) { REQUIRE_FALSE( Catch::Generators::random( TestType{ 0 }, TestType{ 100 } ).isFinite() ); } namespace { struct NotDefaultConstructible { int m_i; explicit NotDefaultConstructible( int i ): m_i( i ){} }; } TEST_CASE( "MapGenerator can handle not default constructible types", "[generators][map]" ) { using namespace Catch::Generators; auto map_generator = map( []( int i ) { return NotDefaultConstructible( i ); }, values({1, 2, 3})); REQUIRE( map_generator.get().m_i == 1 ); REQUIRE( map_generator.next() ); REQUIRE( map_generator.next() ); REQUIRE( map_generator.get().m_i == 3 ); }