Skip to content
Permalink
Browse files

Add GENERATE_COPY and GENERATE_VAR capturing generator macros

  • Loading branch information...
horenmar committed Mar 31, 2019
1 parent b77cec0 commit 3816e99d0c74882eeaaf30ebb48a07a9418f295c
@@ -90,7 +90,11 @@ used with other generators as arguments, such as `auto i = GENERATE(0, 2,
take(100, random(300, 3000)));`. This is useful e.g. if you know that
specific inputs are problematic and want to test them separately/first.

**For safety reasons, you cannot use variables inside the `GENERATE` macro.**
**For safety reasons, you cannot use variables inside the `GENERATE` macro.
This is done because the generator expression _will_ outlive the outside
scope and thus capturing references is dangerous. If you need to use
variables inside the generator expression, make sure you thought through
the lifetime implications and use `GENERATE_COPY` or `GENERATE_REF`.**

You can also override the inferred type by using `as<type>` as the first
argument to the macro. This can be useful when dealing with string literals,
@@ -17,6 +17,7 @@
- Generators: [Create your own generator](../examples/300-Gen-OwnGenerator.cpp)
- Generators: [Use map to convert types in GENERATE expression](../examples/301-Gen-MapTypeConversion.cpp)
- Generators: [Use variables in generator expressions](../examples/310-Gen-VariablesInGenerators.cpp)
- Generators: [Use custom variable capture in generator expressions](../examples/311-Gen-CustomCapture.cpp)


## Planned
@@ -8,41 +8,6 @@

#include <catch2/catch.hpp>

#include <random>

// Lets start by implementing a parametrizable double generator
class RandomDoubleGenerator : public Catch::Generators::IGenerator<double> {
std::minstd_rand m_rand;
std::uniform_real_distribution<> m_dist;
double current_number;
public:

RandomDoubleGenerator(double low, double high):
m_rand(std::random_device{}()),
m_dist(low, high)
{
static_cast<void>(next());
}

double const& get() const override;
bool next() override {
current_number = m_dist(m_rand);
return true;
}
};

// Avoids -Wweak-vtables
double const& RandomDoubleGenerator::get() const {
return current_number;
}


// Also provide a nice shortcut for creating the generator
Catch::Generators::GeneratorWrapper<double> random(double low, double high) {
return Catch::Generators::GeneratorWrapper<double>(std::unique_ptr<Catch::Generators::IGenerator<double>>(new RandomDoubleGenerator(low, high)));
}


TEST_CASE("Generate random doubles across different ranges",
"[generator][example][advanced]") {
// Workaround for old libstdc++
@@ -55,16 +20,12 @@ TEST_CASE("Generate random doubles across different ranges",
}));

// This will not compile (intentionally), because it accesses a variable
// auto number = GENERATE(take(50, random(r.first, r.second)));

// We have to manually register the generators instead
// Notice that we are using value capture in the lambda, to avoid lifetime issues
auto number = Catch::Generators::generate( CATCH_INTERNAL_LINEINFO,
[=]{
using namespace Catch::Generators;
return makeGenerators(take(50, random(std::get<0>(r), std::get<1>(r))));
}
);
// auto number = GENERATE(take(50, random(std::get<0>(r), std::get<1>(r))));

// GENERATE_COPY copies all variables mentioned inside the expression
// thus this will work.
auto number = GENERATE_COPY(take(50, random(std::get<0>(r), std::get<1>(r))));

REQUIRE(std::abs(number) > 0);
}

@@ -0,0 +1,41 @@
// 311-Gen-CustomCapture.cpp
// Shows how to provide custom capture list to the generator expression

// Note that using variables inside generators is dangerous and should
// be done only if you know what you are doing, because the generators
// _WILL_ outlive the variables. Also, even if you know what you are
// doing, you should probably use GENERATE_COPY or GENERATE_REF macros
// instead. However, if your use case requires having a
// per-variable custom capture list, this example shows how to achieve
// that.

#include <catch2/catch.hpp>

TEST_CASE("Generate random doubles across different ranges",
"[generator][example][advanced]") {
// Workaround for old libstdc++
using record = std::tuple<double, double>;
// Set up 3 ranges to generate numbers from
auto r1 = GENERATE(table<double, double>({
record{3, 4},
record{-4, -3},
record{10, 1000}
}));

auto r2(r1);

// This will take r1 by reference and r2 by value.
// Note that there are no advantages for doing so in this example,
// it is done only for expository purposes.
auto number = Catch::Generators::generate( CATCH_INTERNAL_LINEINFO,
[&r1, r2]{
using namespace Catch::Generators;
return makeGenerators(take(50, random(std::get<0>(r1), std::get<1>(r2))));
}
);

REQUIRE(std::abs(number) > 0);
}

// Compiling and running this file will result in 150 successful assertions

@@ -47,6 +47,7 @@ set( SOURCES_IDIOMATIC_TESTS
300-Gen-OwnGenerator.cpp
301-Gen-MapTypeConversion.cpp
310-Gen-VariablesInGenerators.cpp
311-Gen-CustomCapture.cpp
)

# main-s for reporter-specific test sources:
@@ -201,7 +201,10 @@ namespace Generators {
} // namespace Catch

#define GENERATE( ... ) \
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )

Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
#define GENERATE_COPY( ... ) \
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
#define GENERATE_REF( ... ) \
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )

#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
@@ -637,6 +637,22 @@ Matchers.tests.cpp:<line number>: passed: testStringForMatching(), !Contains("di
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), !Contains("substring") for: "this string contains 'abc' as a substring" not contains: "substring"
Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
Exception.tests.cpp:<line number>: failed: thisThrows(), "should fail" for: "expected exception" equals: "should fail"
Generators.tests.cpp:<line number>: passed: values > -6 for: 3 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 4 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 5 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 6 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: -5 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: -4 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 90 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 91 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 92 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 93 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 94 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 95 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 96 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 97 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 98 > -6
Generators.tests.cpp:<line number>: passed: values > -6 for: 99 > -6
Misc.tests.cpp:<line number>: warning: 'This one ran'
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'custom exception'
Tricky.tests.cpp:<line number>: passed: True for: {?}
@@ -1264,6 +1264,6 @@ due to unexpected exception with message:
Why would you throw a std::string?

===============================================================================
test cases: 256 | 190 passed | 62 failed | 4 failed as expected
assertions: 1403 | 1260 passed | 122 failed | 21 failed as expected
test cases: 257 | 191 passed | 62 failed | 4 failed as expected
assertions: 1419 | 1276 passed | 122 failed | 21 failed as expected

@@ -4697,6 +4697,182 @@ Exception.tests.cpp:<line number>: FAILED:
with expansion:
"expected exception" equals: "should fail"

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
3 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
4 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
5 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
6 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
-5 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
-4 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
90 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
91 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
92 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
93 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
94 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
95 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
96 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
97 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
98 > -6

-------------------------------------------------------------------------------
Nested generators and captured variables
-------------------------------------------------------------------------------
Generators.tests.cpp:<line number>
...............................................................................

Generators.tests.cpp:<line number>: PASSED:
REQUIRE( values > -6 )
with expansion:
99 > -6

-------------------------------------------------------------------------------
Nice descriptive name
-------------------------------------------------------------------------------
@@ -10936,6 +11112,6 @@ Misc.tests.cpp:<line number>
Misc.tests.cpp:<line number>: PASSED:

===============================================================================
test cases: 256 | 175 passed | 77 failed | 4 failed as expected
assertions: 1419 | 1260 passed | 138 failed | 21 failed as expected
test cases: 257 | 176 passed | 77 failed | 4 failed as expected
assertions: 1435 | 1276 passed | 138 failed | 21 failed as expected

@@ -4,7 +4,7 @@
<property name="random-seed" value="1"/>
</properties>
loose text artifact
<testsuite name="<exe-name>" errors="17" failures="122" tests="1420" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="122" tests="1436" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#1027" time="{duration}"/>
@@ -454,6 +454,7 @@ Matchers.tests.cpp:<line number>
Exception.tests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="Nested generators and captured variables" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Nice descriptive name" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Non-std exceptions can be translated" time="{duration}">
<error type="TEST_CASE">
Oops, something went wrong.

0 comments on commit 3816e99

Please sign in to comment.
You can’t perform that action at this time.