Merge pull request #1516 from catchorg/dev-generators-take2

This replaces the old interface with a final one.
This commit is contained in:
Martin Hořeňovský 2019-01-31 14:24:30 +01:00 committed by GitHub
commit 63d1a96908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2728 additions and 2010 deletions

View File

@ -1,50 +1,125 @@
<a id="top"></a>
# Data Generators
_Generators are currently considered an experimental feature and their
API can change between versions freely._
Data generators (also known as _data driven/parametrized test cases_)
let you reuse the same set of assertions across different input values.
In Catch2, this means that they respect the ordering and nesting
of the `TEST_CASE` and `SECTION` macros.
How does combining generators and test cases work might be better
explained by an example:
of the `TEST_CASE` and `SECTION` macros, and their nested sections
are run once per each value in a generator.
This is best explained with an example:
```cpp
TEST_CASE("Generators") {
auto i = GENERATE( range(1, 11) );
SECTION( "Some section" ) {
auto j = GENERATE( range( 11, 21 ) );
REQUIRE(i < j);
auto i = GENERATE(1, 2, 3);
SECTION("one") {
auto j = GENERATE( -3, -2, -1 );
REQUIRE(j < i);
}
}
```
the assertion will be checked 100 times, because there are 10 possible
values for `i` (1, 2, ..., 10) and for each of them, there are 10 possible
values for `j` (11, 12, ..., 20).
The assertion in this test case will be run 9 times, because there
are 3 possible values for `i` (1, 2, and 3) and there are 3 possible
values for `j` (-3, -2, and -1).
There are 2 parts to generators in Catch2, the `GENERATE` macro together
with the already provided generators, and the `IGenerator<T>` interface
that allows users to implement their own generators.
## Provided generators
Catch2's provided generator functionality consists of three parts,
* `GENERATE` macro, that serves to integrate generator expression with
a test case,
* 2 fundamental generators
* `ValueGenerator<T>` -- contains only single element
* `ValuesGenerator<T>` -- contains multiple elements
* 4 generic generators that modify other generators
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
for which the predicate returns "false"
* `TakeGenerator<T>` -- takes first `n` elements from a generator
* `RepeatGenerator<T>` -- repeats output from a generator `n` times
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
on elements from a different generator
The generators also have associated helper functions that infer their
type, making their usage much nicer. These are
* `value(T&&)` for `ValueGenerator<T>`
* `values(std::initializer_list<T>)` for `ValuesGenerator<T>`
* `filter(predicate, GeneratorWrapper<T>&&)` for `FilterGenerator<T, Predicate>`
* `take(count, GeneratorWrapper<T>&&)` for `TakeGenerator<T>`
* `repeat(repeats, GeneratorWrapper<T>&&)` for `RepeatGenerator<T>`
* `map(func, GeneratorWrapper<T>&&)` for `MapGenerator<T, T, Func>` (map `T` to `T`)
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
And can be used as shown in the example below to create a generator
that returns 100 odd random number:
You can also combine multiple generators by concatenation:
```cpp
static int square(int x) { return x * x; }
TEST_CASE("Generators 2") {
auto i = GENERATE(0, 1, -1, range(-20, -10), range(10, 20));
CAPTURE(i);
REQUIRE(square(i) >= 0);
TEST_CASE("Generating random ints", "[example][generator]") {
SECTION("Deducing functions") {
auto i = GENERATE(take(100, filter([](int i) { return i % 2 == 1; }, random(-100, 100))));
REQUIRE(i > -100);
REQUIRE(i < 100);
REQUIRE(i % 2 == 1);
}
}
```
This will call `square` with arguments `0`, `1`, `-1`, `-20`, ..., `-11`,
`10`, ..., `19`.
_Note that `random` is currently not a part of the first-party generators_.
----------
Because of the experimental nature of the current Generator implementation,
we won't list all of the first-party generators in Catch2. Instead you
should look at our current usage tests in
[projects/SelfTest/UsageTests/Generators.tests.cpp](/projects/SelfTest/UsageTests/Generators.tests.cpp).
For implementing your own generators, you can look at their implementation in
[include/internal/catch_generators.hpp](/include/internal/catch_generators.hpp).
Apart from registering generators with Catch2, the `GENERATE` macro has
one more purpose, and that is to provide simple way of generating trivial
generators, as seen in the first example on this page, where we used it
as `auto i = GENERATE(1, 2, 3);`. This usage converted each of the three
literals into a single `ValueGenerator<int>` and then placed them all in
a special generator that concatenates other generators. It can also be
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.**
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,
if you want them to come out as `std::string`:
```cpp
TEST_CASE("type conversion", "[generators]") {
auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");`
REQUIRE(str.size() > 0);
}
```
## Generator interface
You can also implement your own generators, by deriving from the
`IGenerator<T>` interface:
```cpp
template<typename T>
struct IGenerator : GeneratorUntypedBase {
// via GeneratorUntypedBase:
// Attempts to move the generator to the next element.
// Returns true if successful (and thus has another element that can be read)
virtual bool next() = 0;
// Precondition:
// The generator is either freshly constructed or the last call to next() returned true
virtual T const& get() const = 0;
};
```
However, to be able to use your custom generator inside `GENERATE`, it
will need to be wrapped inside a `GeneratorWrapper<T>`.
`GeneratorWrapper<T>` is a value wrapper around a
`std::unique_ptr<IGenerator<T>>`.
For full example of implementing your own generator, look into Catch2's
examples, specifically
[Generators: Create your own generator](../examples/300-Gen-OwnGenerator.cpp).

View File

@ -14,6 +14,9 @@
- Report: [TeamCity reporter](../examples/207-Rpt-TeamCityReporter.cpp)
- Listener: [Listeners](../examples/210-Evt-EventListeners.cpp)
- Configuration: [Provide your own output streams](../examples/231-Cfg-OutputStreams.cpp)
- Generators: [Create your own generator](../examples/300-Gen-OwnGenerator.cpp)
- Generators: [Use variables in generator expressions](../examples/310-Gen-VariablesInGenerators.cpp)
## Planned

View File

@ -0,0 +1,59 @@
// 300-Gen-OwnGenerator.cpp
// Shows how to define a custom generator.
// Specifically we will implement a random number generator for integers
// It will have infinite capacity and settable lower/upper bound
#include <catch2/catch.hpp>
#include <random>
// This class shows how to implement a simple generator for Catch tests
class RandomIntGenerator : public Catch::Generators::IGenerator<int> {
std::minstd_rand m_rand;
std::uniform_int_distribution<> m_dist;
int current_number;
public:
RandomIntGenerator(int low, int high):
m_rand(std::random_device{}()),
m_dist(low, high)
{
static_cast<void>(next());
}
int const& get() const override;
bool next() override {
current_number = m_dist(m_rand);
return true;
}
};
// Avoids -Wweak-vtables
int const& RandomIntGenerator::get() const {
return current_number;
}
// This helper function provides a nicer UX when instantiating the generator
// Notice that it returns an instance of GeneratorWrapper<int>, which
// is a value-wrapper around std::unique_ptr<IGenerator<int>>.
Catch::Generators::GeneratorWrapper<int> random(int low, int high) {
return Catch::Generators::GeneratorWrapper<int>(std::unique_ptr<Catch::Generators::IGenerator<int>>(new RandomIntGenerator(low, high)));
}
// The two sections in this test case are equivalent, but the first one
// is much more readable/nicer to use
TEST_CASE("Generating random ints", "[example][generator]") {
SECTION("Nice UX") {
auto i = GENERATE(take(100, random(-100, 100)));
REQUIRE(i >= -100);
REQUIRE(i <= 100);
}
SECTION("Creating the random generator directly") {
auto i = GENERATE(take(100, GeneratorWrapper<int>(std::unique_ptr<IGenerator<int>>(new RandomIntGenerator(-100, 100)))));
REQUIRE(i >= -100);
REQUIRE(i <= 100);
}
}
// Compiling and running this file will result in 400 successful assertions

View File

@ -0,0 +1,72 @@
// 310-Gen-VariablesInGenerator.cpp
// Shows how to use variables when creating generators.
// 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 -- thus they should be either captured
// by value directly, or copied by the generators during construction.
#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++
using record = std::tuple<double, double>;
// Set up 3 ranges to generate numbers from
auto r = GENERATE(table<double, double>({
record{3, 4},
record{-4, -3},
record{10, 1000}
}));
// 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))));
}
);
REQUIRE(std::abs(number) > 0);
}
// Compiling and running this file will result in 150 successful assertions

View File

@ -44,6 +44,8 @@ set( SOURCES_IDIOMATIC_TESTS
110-Fix-ClassFixture.cpp
120-Bdd-ScenarioGivenWhenThen.cpp
210-Evt-EventListeners.cpp
300-Gen-OwnGenerator.cpp
310-Gen-VariablesInGenerators.cpp
)
# main-s for reporter-specific test sources:

View File

@ -16,35 +16,17 @@ namespace Catch {
IGeneratorTracker::~IGeneratorTracker() {}
const char* GeneratorException::what() const noexcept {
return m_msg;
}
namespace Generators {
GeneratorBase::~GeneratorBase() {}
std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) {
assert( selectionSize <= sourceSize );
std::vector<size_t> indices;
indices.reserve( selectionSize );
std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 );
std::set<size_t> seen;
// !TBD: improve this algorithm
while( indices.size() < selectionSize ) {
auto index = uid( rng() );
if( seen.insert( index ).second )
indices.push_back( index );
}
return indices;
}
GeneratorUntypedBase::~GeneratorUntypedBase() {}
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
return getResultCapture().acquireGeneratorTracker( lineInfo );
}
template<>
auto all<int>() -> Generator<int> {
return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() );
}
} // namespace Generators
} // namespace Catch

View File

@ -16,8 +16,21 @@
#include <cassert>
#include <utility>
#include <exception>
namespace Catch {
class GeneratorException : public std::exception {
const char* const m_msg = "";
public:
GeneratorException(const char* msg):
m_msg(msg)
{}
const char* what() const noexcept override final;
};
namespace Generators {
// !TBD move this into its own location?
@ -29,202 +42,312 @@ namespace Generators {
}
template<typename T>
struct IGenerator {
virtual ~IGenerator() {}
virtual auto get( size_t index ) const -> T = 0;
struct IGenerator : GeneratorUntypedBase {
virtual ~IGenerator() = default;
// Returns the current element of the generator
//
// \Precondition The generator is either freshly constructed,
// or the last call to `next()` returned true
virtual T const& get() const = 0;
using type = T;
};
template<typename T>
class SingleValueGenerator : public IGenerator<T> {
class SingleValueGenerator final : public IGenerator<T> {
T m_value;
public:
SingleValueGenerator( T const& value ) : m_value( value ) {}
SingleValueGenerator(T const& value) : m_value( value ) {}
SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
auto get( size_t ) const -> T override {
T const& get() const override {
return m_value;
}
bool next() override {
return false;
}
};
template<typename T>
class FixedValuesGenerator : public IGenerator<T> {
class FixedValuesGenerator final : public IGenerator<T> {
std::vector<T> m_values;
size_t m_idx = 0;
public:
FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
auto get( size_t index ) const -> T override {
return m_values[index];
T const& get() const override {
return m_values[m_idx];
}
bool next() override {
++m_idx;
return m_idx < m_values.size();
}
};
template<typename T>
class RangeGenerator : public IGenerator<T> {
T const m_first;
T const m_last;
public:
RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) {
assert( m_last > m_first );
}
auto get( size_t index ) const -> T override {
// ToDo:: introduce a safe cast to catch potential overflows
return static_cast<T>(m_first+index);
}
};
template<typename T>
struct NullGenerator : IGenerator<T> {
auto get( size_t ) const -> T override {
CATCH_INTERNAL_ERROR("A Null Generator is always empty");
}
};
template<typename T>
class Generator {
template <typename T>
class GeneratorWrapper final {
std::unique_ptr<IGenerator<T>> m_generator;
size_t m_size;
public:
Generator( size_t size, std::unique_ptr<IGenerator<T>> generator )
: m_generator( std::move( generator ) ),
m_size( size )
GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
m_generator(std::move(generator))
{}
auto size() const -> size_t { return m_size; }
auto operator[]( size_t index ) const -> T {
assert( index < m_size );
return m_generator->get( index );
T const& get() const {
return m_generator->get();
}
bool next() {
return m_generator->next();
}
};
std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize );
template <typename T>
GeneratorWrapper<T> value(T&& value) {
return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
}
template <typename T>
GeneratorWrapper<T> values(std::initializer_list<T> values) {
return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
}
template<typename T>
class GeneratorRandomiser : public IGenerator<T> {
Generator<T> m_baseGenerator;
class Generators : public IGenerator<T> {
std::vector<GeneratorWrapper<T>> m_generators;
size_t m_current = 0;
std::vector<size_t> m_indices;
public:
GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems )
: m_baseGenerator( std::move( baseGenerator ) ),
m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) )
{}
auto get( size_t index ) const -> T override {
return m_baseGenerator[m_indices[index]];
void populate(GeneratorWrapper<T>&& generator) {
m_generators.emplace_back(std::move(generator));
}
};
template<typename T>
struct RequiresASpecialisationFor;
template<typename T>
auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); }
template<>
auto all<int>() -> Generator<int>;
template<typename T>
auto range( T const& first, T const& last ) -> Generator<T> {
return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) );
}
template<typename T>
auto random( T const& first, T const& last ) -> Generator<T> {
auto gen = range( first, last );
auto size = gen.size();
return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) );
}
template<typename T>
auto random( size_t size ) -> Generator<T> {
return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) );
}
template<typename T>
auto values( std::initializer_list<T> values ) -> Generator<T> {
return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) );
}
template<typename T>
auto value( T const& val ) -> Generator<T> {
return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) );
}
template<typename T>
auto as() -> Generator<T> {
return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() );
}
template<typename... Ts>
auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> {
return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) );
}
template<typename T>
struct Generators : GeneratorBase {
std::vector<Generator<T>> m_generators;
using type = T;
Generators() : GeneratorBase( 0 ) {}
void populate( T&& val ) {
m_size += 1;
m_generators.emplace_back( value( std::move( val ) ) );
void populate(T&& val) {
m_generators.emplace_back(value(std::move(val)));
}
template<typename U>
void populate( U&& val ) {
populate( T( std::move( val ) ) );
void populate(U&& val) {
populate(T(std::move(val)));
}
void populate( Generator<T>&& generator ) {
m_size += generator.size();
m_generators.emplace_back( std::move( generator ) );
}
template<typename U, typename... Gs>
void populate( U&& valueOrGenerator, Gs... moreGenerators ) {
populate( std::forward<U>( valueOrGenerator ) );
populate( std::forward<Gs>( moreGenerators )... );
void populate(U&& valueOrGenerator, Gs... moreGenerators) {
populate(std::forward<U>(valueOrGenerator));
populate(std::forward<Gs>(moreGenerators)...);
}
auto operator[]( size_t index ) const -> T {
size_t sizes = 0;
for( auto const& gen : m_generators ) {
auto localIndex = index-sizes;
sizes += gen.size();
if( index < sizes )
return gen[localIndex];
public:
template <typename... Gs>
Generators(Gs... moreGenerators) {
m_generators.reserve(sizeof...(Gs));
populate(std::forward<Gs>(moreGenerators)...);
}
T const& get() const override {
return m_generators[m_current].get();
}
bool next() override {
if (m_current >= m_generators.size()) {
return false;
}
CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
const bool current_status = m_generators[m_current].next();
if (!current_status) {
++m_current;
}
return m_current < m_generators.size();
}
};
template<typename... Ts>
GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
return values<std::tuple<Ts...>>( tuples );
}
// Tag type to signal that a generator sequence should convert arguments to a specific type
template <typename T>
struct as {};
template<typename T, typename... Gs>
auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
Generators<T> generators;
generators.m_generators.reserve( 1+sizeof...(Gs) );
generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... );
return generators;
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
}
template<typename T>
auto makeGenerators( Generator<T>&& generator ) -> Generators<T> {
Generators<T> generators;
generators.populate( std::move( generator ) );
return generators;
auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
return Generators<T>(std::move(generator));
}
template<typename T, typename... Gs>
auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
}
template<typename T, typename U, typename... Gs>
auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> {
auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> {
return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
}
template <typename T>
class TakeGenerator : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
size_t m_returned = 0;
size_t m_target;
public:
TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
m_generator(std::move(generator)),
m_target(target)
{
assert(target != 0 && "Empty generators are not allowed");
}
T const& get() const override {
return m_generator.get();
}
bool next() override {
++m_returned;
if (m_returned >= m_target) {
return false;
}
const auto success = m_generator.next();
// If the underlying generator does not contain enough values
// then we cut short as well
if (!success) {
m_returned = m_target;
}
return success;
}
};
template <typename T>
GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
}
template <typename T, typename Predicate>
class FilterGenerator : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
Predicate m_predicate;
public:
template <typename P = Predicate>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
m_generator(std::move(generator)),
m_predicate(std::forward<P>(pred))
{
if (!m_predicate(m_generator.get())) {
// It might happen that there are no values that pass the
// filter. In that case we throw an exception.
auto has_initial_value = next();
if (!has_initial_value) {
Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
}
}
}
T const& get() const override {
return m_generator.get();
}
bool next() override {
bool success = m_generator.next();
if (!success) {
return false;
}
while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
return success;
}
};
template <typename T, typename Predicate>
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
}
template <typename T>
class RepeatGenerator : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
mutable std::vector<T> m_returned;
size_t m_target_repeats;
size_t m_current_repeat = 0;
size_t m_repeat_index = 0;
public:
RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
m_generator(std::move(generator)),
m_target_repeats(repeats)
{
assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
}
T const& get() const override {
if (m_current_repeat == 0) {
m_returned.push_back(m_generator.get());
return m_returned.back();
}
return m_returned[m_repeat_index];
}
bool next() override {
// There are 2 basic cases:
// 1) We are still reading the generator
// 2) We are reading our own cache
// In the first case, we need to poke the underlying generator.
// If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
if (m_current_repeat == 0) {
const auto success = m_generator.next();
if (!success) {
++m_current_repeat;
}
return m_current_repeat < m_target_repeats;
}
// In the second case, we need to move indices forward and check that we haven't run up against the end
++m_repeat_index;
if (m_repeat_index == m_returned.size()) {
m_repeat_index = 0;
++m_current_repeat;
}
return m_current_repeat < m_target_repeats;
}
};
template <typename T>
GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
}
template <typename T, typename U, typename Func>
class MapGenerator : public IGenerator<T> {
// TBD: provide static assert for mapping function, for friendly error message
GeneratorWrapper<U> m_generator;
Func m_function;
// To avoid returning dangling reference, we have to save the values
T m_cache;
public:
template <typename F2 = Func>
MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
m_generator(std::move(generator)),
m_function(std::forward<F2>(function)),
m_cache(m_function(m_generator.get()))
{}
T const& get() const override {
return m_cache;
}
bool next() override {
const auto success = m_generator.next();
if (success) {
m_cache = m_function(m_generator.get());
}
return success;
}
};
template <typename T, typename U, typename Func>
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
return GeneratorWrapper<T>(
pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
);
}
template <typename T, typename Func>
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(
pf::make_unique<MapGenerator<T, T, Func>>(std::forward<Func>(function), std::move(generator))
);
}
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
@ -232,15 +355,16 @@ namespace Generators {
// Note: The type after -> is weird, because VS2015 cannot parse
// the expression used in the typedef inside, when it is in
// return type. Yeah, ¯\_(ツ)_/¯
auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) {
auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
using UnderlyingType = typename decltype(generatorExpression())::type;
IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
if( !tracker.hasGenerator() )
tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) );
if (!tracker.hasGenerator()) {
tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
}
auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() );
return generator[tracker.getIndex()];
auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
return generator.get();
}
} // namespace Generators

View File

@ -13,16 +13,17 @@
namespace Catch {
namespace Generators {
class GeneratorBase {
protected:
size_t m_size = 0;
class GeneratorUntypedBase {
public:
GeneratorBase( size_t size ) : m_size( size ) {}
virtual ~GeneratorBase();
auto size() const -> size_t { return m_size; }
GeneratorUntypedBase() = default;
virtual ~GeneratorUntypedBase();
// Attempts to move the generator to the next element
//
// Returns true iff the move succeeded (and a valid element
// can be retrieved).
virtual bool next() = 0;
};
using GeneratorBasePtr = std::unique_ptr<GeneratorBase>;
using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
} // namespace Generators
@ -31,7 +32,6 @@ namespace Catch {
virtual auto hasGenerator() const -> bool = 0;
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
virtual auto getIndex() const -> std::size_t = 0;
};
} // namespace Catch

View File

@ -14,7 +14,6 @@ namespace Catch {
namespace Generators {
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
size_t m_index = static_cast<size_t>( -1 );
GeneratorBasePtr m_generator;
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
@ -28,7 +27,7 @@ namespace Catch {
ITracker& currentTracker = ctx.currentTracker();
if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isIndexTracker() );
assert( childTracker->isGeneratorTracker() );
tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
}
else {
@ -37,28 +36,24 @@ namespace Catch {
}
if( !ctx.completedCycle() && !tracker->isComplete() ) {
if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
tracker->moveNext();
tracker->open();
}
return *tracker;
}
void moveNext() {
m_index++;
m_children.clear();
}
// TrackerBase interface
bool isIndexTracker() const override { return true; }
bool isGeneratorTracker() const override { return true; }
auto hasGenerator() const -> bool override {
return !!m_generator;
}
void close() override {
TrackerBase::close();
if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 )
// Generator interface only finds out if it has another item on atual move
if (m_runState == CompletedSuccessfully && m_generator->next()) {
m_children.clear();
m_runState = Executing;
}
}
// IGeneratorTracker interface
@ -68,9 +63,6 @@ namespace Catch {
void setGenerator( GeneratorBasePtr&& generator ) override {
m_generator = std::move( generator );
}
auto getIndex() const -> size_t override {
return m_index;
}
};
GeneratorTracker::~GeneratorTracker() {}
}

View File

@ -121,7 +121,7 @@ namespace TestCaseTracking {
}
bool TrackerBase::isSectionTracker() const { return false; }
bool TrackerBase::isIndexTracker() const { return false; }
bool TrackerBase::isGeneratorTracker() const { return false; }
void TrackerBase::open() {
m_runState = Executing;

View File

@ -54,7 +54,7 @@ namespace TestCaseTracking {
// Debug/ checking
virtual bool isSectionTracker() const = 0;
virtual bool isIndexTracker() const = 0;
virtual bool isGeneratorTracker() const = 0;
};
class TrackerContext {
@ -120,7 +120,7 @@ namespace TestCaseTracking {
void openChild() override;
bool isSectionTracker() const override;
bool isIndexTracker() const override;
bool isGeneratorTracker() const override;
void open();

View File

@ -57,106 +57,87 @@ Tricky.tests.cpp:<line number>: passed: !is_true<false>::value for: true
Tricky.tests.cpp:<line number>: passed: !!is_true<true>::value for: true
Tricky.tests.cpp:<line number>: passed: is_true<true>::value for: true
Tricky.tests.cpp:<line number>: passed: !(is_true<false>::value) for: !false
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 101
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 102
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 103
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 104
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 105
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 106
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 107
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 108
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 109
Generators.tests.cpp:<line number>: passed: x < y for: 10 < 110
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 1 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 2 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 4
Generators.tests.cpp:<line number>: passed: y < z for: 4 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 5
Generators.tests.cpp:<line number>: passed: y < z for: 5 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 7
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 8
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 6
Generators.tests.cpp:<line number>: passed: y < z for: 6 < 9
Generators.tests.cpp:<line number>: passed: x < z for: 3 < 9
Class.tests.cpp:<line number>: failed: s == "world" for: "hello" == "world"
Class.tests.cpp:<line number>: passed: s == "hello" for: "hello" == "hello"
Class.tests.cpp:<line number>: failed: Template_Fixture_2<TestType>::m_a.size() == 1 for: 0 == 1
@ -418,48 +399,98 @@ Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, 0.f)
Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, -1.f), std::domain_error
Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, 0)
Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, -1), std::domain_error
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 8'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 9'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 10'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 2'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 3.141'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 1.379'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 8'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 9'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 10'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 2'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 3.141'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 1.379'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 8'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 9'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 10'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 2'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 3.141'
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 1.379'
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 4 for: 4 == 4
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[2] == 4 for: 4 == 4
GeneratorsImpl.tests.cpp:<line number>: passed: gen[3] == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 4 for: 4 == 4
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[2] == 9 for: 9 == 9
GeneratorsImpl.tests.cpp:<line number>: passed: gen[3] == 7 for: 7 == 7
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: base->size() == 4 for: 4 == 4
GeneratorsImpl.tests.cpp:<line number>: passed: typed for: 0x<hex digits>
GeneratorsImpl.tests.cpp:<line number>: passed: typed->size() == 4 for: 4 == 4
GeneratorsImpl.tests.cpp:<line number>: passed: (*typed)[0] == 7 for: 7 == 7
GeneratorsImpl.tests.cpp:<line number>: passed: (*typed)[3] == 11 for: 11 == 11
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i < 4 for: 1 < 4
Generators.tests.cpp:<line number>: passed: i < 4 for: 2 < 4
Generators.tests.cpp:<line number>: passed: i < 4 for: 3 < 4
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: i.size() == 1 for: 1 == 1
Generators.tests.cpp:<line number>: passed: i.size() == 1 for: 1 == 1
Generators.tests.cpp:<line number>: passed: i.size() == 1 for: 1 == 1
Generators.tests.cpp:<line number>: passed: j > 0 for: 1 > 0
Generators.tests.cpp:<line number>: passed: j > 0 for: 2 > 0
Generators.tests.cpp:<line number>: passed: j > 0 for: 3 > 0
Generators.tests.cpp:<line number>: passed: j > 0 for: 1 > 0
Generators.tests.cpp:<line number>: passed: j > 0 for: 2 > 0
Generators.tests.cpp:<line number>: passed: j > 0 for: 3 > 0
Generators.tests.cpp:<line number>: passed: j < i for: -3 < 1
Generators.tests.cpp:<line number>: passed: j < i for: -2 < 1
Generators.tests.cpp:<line number>: passed: j < i for: -1 < 1
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 4 > 1
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 4 > 2
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 4 > 3
Generators.tests.cpp:<line number>: passed: j < i for: -3 < 2
Generators.tests.cpp:<line number>: passed: j < i for: -2 < 2
Generators.tests.cpp:<line number>: passed: j < i for: -1 < 2
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 8 > 1
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 8 > 2
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 8 > 3
Generators.tests.cpp:<line number>: passed: j < i for: -3 < 3
Generators.tests.cpp:<line number>: passed: j < i for: -2 < 3
Generators.tests.cpp:<line number>: passed: j < i for: -1 < 3
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 1
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 2
Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 123 for: 123 == 123
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 5 for: 5 == 5
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 4 for: 4 == 4
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 0 for: 0 == 0
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get().size() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == "aa" for: "aa" == "aa"
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == "bb" for: "bb" == "bb"
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == "cc" for: "cc" == "cc"
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: filter([] (int) { return false; }, value(1)), Catch::GeneratorException
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2.0 for: 2.0 == 2.0
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 4.0 for: 4.0 == 4.0
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 6.0 for: 6.0 == 6.0
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 2 for: 2 == 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
Approx.tests.cpp:<line number>: passed: d >= Approx( 1.22 ) for: 1.23 >= Approx( 1.22 )
Approx.tests.cpp:<line number>: passed: d >= Approx( 1.23 ) for: 1.23 >= Approx( 1.23 )
Approx.tests.cpp:<line number>: passed: !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 ))
@ -1274,6 +1305,10 @@ Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 3 =
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 3 == 3
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 5 == 5
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 4 == 4
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'Why would you throw a std::string?'
Misc.tests.cpp:<line number>: passed: result == "\"wide load\"" for: ""wide load"" == ""wide load""
Misc.tests.cpp:<line number>: passed: result == "\"wide load\"" for: ""wide load"" == ""wide load""

View File

@ -1170,6 +1170,6 @@ due to unexpected exception with message:
Why would you throw a std::string?
===============================================================================
test cases: 243 | 183 passed | 56 failed | 4 failed as expected
assertions: 1262 | 1126 passed | 115 failed | 21 failed as expected
test cases: 245 | 185 passed | 56 failed | 4 failed as expected
assertions: 1297 | 1161 passed | 115 failed | 21 failed as expected

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="17" failures="113" tests="1277" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="113" tests="1312" 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}"/>
@ -70,7 +70,7 @@ Condition.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="(unimplemented) static bools can be evaluated/negation" time="{duration}"/>
<testcase classname="<exe-name>.global" name="(unimplemented) static bools can be evaluated/double negation" time="{duration}"/>
<testcase classname="<exe-name>.global" name="(unimplemented) static bools can be evaluated/direct" time="{duration}"/>
<testcase classname="<exe-name>.global" name="10x10 ints" time="{duration}"/>
<testcase classname="<exe-name>.global" name="3x3x3 ints" time="{duration}"/>
<testcase classname="<exe-name>.TestClass" name="A METHOD_AS_TEST_CASE based test run that fails" time="{duration}">
<failure message="&quot;hello&quot; == &quot;world&quot;" type="REQUIRE">
Class.tests.cpp:<line number>
@ -339,14 +339,23 @@ Message.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/ULPs" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Constructor validation" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators/one" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators/two" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/range" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/fixed values" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/combined" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/values" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/values2" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/type erasure" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- adapters/Filtering by predicate" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- adapters/Shortening a range" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- adapters/Transforming elements/Same type" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- adapters/Transforming elements/Different type" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- adapters/Repeating a generator" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- simple/one" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators -- simple/two" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Single value" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Preset values" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Generator combinator" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Explicitly typed generator sequence" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Filter generator" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator/Take less" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Take generator/Take more" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Map" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Repeat/Singular repeat" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators internals/Repeat/Actual repeat" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Greater-than inequalities with different epsilons" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}">
@ -938,6 +947,7 @@ Tricky.tests.cpp:<line number>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_maker_and_operator> )" time="{duration}"/>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_operator> )" time="{duration}"/>
<testcase classname="<exe-name>.global" name="strlen3" time="{duration}"/>
<testcase classname="<exe-name>.global" name="tables" time="{duration}"/>
<testcase classname="<exe-name>.global" name="thrown std::strings are translated" time="{duration}">
<error type="TEST_CASE">
Why would you throw a std::string?

File diff suppressed because it is too large Load Diff

View File

@ -1,93 +1,103 @@
#include "catch.hpp"
// Tests of generartor implementation details
TEST_CASE("Generators impl", "[impl]") {
// Tests of generator implementation details
TEST_CASE("Generators internals", "[generators][internals]") {
using namespace Catch::Generators;
SECTION( "range" ) {
auto gen = range(1,3);
CHECK( gen.size() == 2 );
CHECK( gen[0] == 1 );
CHECK( gen[1] == 2 );
SECTION("Single value") {
auto gen = value(123);
REQUIRE(gen.get() == 123);
REQUIRE_FALSE(gen.next());
}
SECTION( "fixed values" ) {
auto gen = values( { 3, 1, 4, 1 } );
CHECK( gen.size() == 4 );
CHECK( gen[0] == 3 );
CHECK( gen[1] == 1 );
CHECK( gen[2] == 4 );
CHECK( gen[3] == 1 );
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( "combined" ) {
auto gen = makeGenerators( range( 1, 3 ), values( { 9, 7 } ) );
CHECK( gen.size() == 4 );
CHECK( gen[0] == 1 );
CHECK( gen[1] == 2 );
CHECK( gen[2] == 9 );
CHECK( gen[3] == 7 );
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( "values" ) {
auto gen = makeGenerators( 3, 1 );
CHECK( gen.size() == 2 );
CHECK( gen[0] == 3 );
CHECK( gen[1] == 1 );
SECTION("Explicitly typed generator sequence") {
auto gen = makeGenerators(as<std::string>{}, "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( "values2" ) {
auto gen = makeGenerators( 3, 1 );
SECTION("Filter generator") {
// Normal usage
auto gen = filter([] (int i) { return i != 2; }, values({ 2, 1, 2, 3, 2, 2 }));
REQUIRE(gen.get() == 1);
REQUIRE(gen.next());
REQUIRE(gen.get() == 3);
REQUIRE_FALSE(gen.next());
CHECK( gen.size() == 2 );
CHECK( gen[0] == 3 );
CHECK( gen[1] == 1 );
// Completely filtered-out generator should throw on construction
REQUIRE_THROWS_AS(filter([] (int) { return false; }, value(1)), Catch::GeneratorException);
}
SECTION( "type erasure" ) {
auto gen = makeGenerators( range( 7, 10 ), 11 );
// Make type erased version
auto dynCopy = pf::make_unique<Generators<int>>( std::move( gen ) );
std::unique_ptr<GeneratorBase const> base = std::move( dynCopy );
// Only thing we can do is ask for the size
CHECK( base->size() == 4 );
// Restore typed version
auto typed = dynamic_cast<Generators<int> const*>( base.get() );
REQUIRE( typed );
CHECK( typed->size() == 4 );
CHECK( (*typed)[0] == 7 );
CHECK( (*typed)[3] == 11 );
}
}
TEST_CASE("Generators impl - random", "[approvals]") {
using namespace Catch::Generators;
SECTION( "random range" ) {
auto gen = random( 3, 9 );
CHECK( gen.size() == 6 );
for( size_t i = 0; i < 6; ++i ) {
CHECK( gen[i] >= 3 );
CHECK( gen[i] <= 8 );
if( i > 0 )
CHECK( gen[i] != gen[i-1] );
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( "random selection" ) {
auto gen = random<int>( 10 );
CHECK( gen.size() == 10 );
for( size_t i = 0; i < 10; ++i ) {
if( i > 0 )
CHECK( gen[i] != gen[i-1] );
SECTION("Map") {
auto gen = map<double>([] (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());
}
}
}

View File

@ -1,75 +1,63 @@
#include "catch.hpp"
// Examples of usage of Generators
#include <cstring>
// This test doesn't do much - it just shows how you can have several generators, of different
// types (ie `i` and `j` are different types), can be sequenced using `,` and
// can be expressed as named generators (like range) or as individual values.
// Generators can be mixed with SECTIONs.
// At time of writing the generated values are not automatically reported as part of the test
// name or associated values - so we explicitly CAPTURE then (run this with `-s` to see them).
// We could also incorporate them into the section names using DYNAMIC_SECTION. See the BDD
// example later for more information.
TEST_CASE("Generators") {
auto i = GENERATE( as<std::string>(), "a", "b", "c" );
SECTION( "one" ) {
auto j = GENERATE( range( 8, 11 ), 2 );
CAPTURE( i, j );
SUCCEED();
// Generators and sections can be nested freely
TEST_CASE("Generators -- simple", "[generators]") {
auto i = GENERATE(1, 2, 3);
SECTION("one") {
auto j = GENERATE(values({ -3, -2, -1 }));
REQUIRE(j < i);
}
SECTION( "two" ) {
auto j = GENERATE( 3.141, 1.379 );
CAPTURE( i, j );
SUCCEED();
SECTION("two") {
// You can also explicitly set type for generators via Catch::Generators::as
auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");
REQUIRE(4u * i > str.size());
}
}
// This one generates the cross-product of two ranges.
// It's mostly here to demonstrate the performance which, at time of writing,
// leaves a lot to be desired.
TEST_CASE( "100x100 ints", "[.][approvals]" ) {
auto x = GENERATE( range( 0,100 ) );
auto y = GENERATE( range( 200,300 ) );
CHECK( x < y );
// You can create a cartesian-product of generators by creating multiple ones
TEST_CASE("3x3x3 ints", "[generators]") {
auto x = GENERATE(1, 2, 3);
auto y = GENERATE(4, 5, 6);
auto z = GENERATE(7, 8, 9);
// These assertions will be run 27 times (3x3x3)
CHECK(x < y);
CHECK(y < z);
REQUIRE(x < z);
}
// smaller version
TEST_CASE( "10x10 ints" ) {
auto x = GENERATE( range( 1,11 ) );
auto y = GENERATE( range( 101, 111 ) );
// You can also create data tuples
TEST_CASE("tables", "[generators]") {
// Note that this will not compile with libstdc++ older than libstdc++6
// See https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
// for possible workarounds
// auto data = GENERATE(table<char const*, int>({
// {"first", 5},
// {"second", 6},
// {"third", 5},
// {"etc...", 6}
// }));
CHECK( x < y );
// Workaround for the libstdc++ bug mentioned above
using tuple_type = std::tuple<char const*, int>;
auto data = GENERATE(table<char const*, int>({
tuple_type{"first", 5},
tuple_type{"second", 6},
tuple_type{"third", 5},
tuple_type{"etc...", 6}
}));
REQUIRE(strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)));
}
// Some of the following tests use structured bindings for convenience and so are
// conditionally compiled using the de-facto (and soon to be formally) standard
// feature macros
#ifdef __cpp_structured_bindings
// One way to do pairs of values (actual/ expected?)
// For a simple case like this I'd recommend writing out a series of REQUIREs
// but it demonstrates a possible usage.
// Spelling out the pair like this is a bit verbose, so read on for better examples
// - the use of structured bindings here is an optional convenience
TEST_CASE( "strlen", "[approvals]" ) {
auto [test_input, expected] = GENERATE( values<std::pair<std::string_view, size_t>>({
{"one", 3},
{"two", 3},
{"three", 5},
{"four", 4}
}));
REQUIRE( test_input.size() == expected );
}
// A nicer way to do pairs (or more) of values - using the table generator.
// Note, you must specify the types up-front.
TEST_CASE( "strlen2", "[approvals]" ) {
// Structured bindings make the table utility much nicer to use
TEST_CASE( "strlen2", "[approvals][generators]" ) {
auto [test_input, expected] = GENERATE( table<std::string, size_t>({
{"one", 3},
{"two", 3},
@ -81,11 +69,11 @@ TEST_CASE( "strlen2", "[approvals]" ) {
}
#endif
// An alternate way of doing data tables without structure bindings
// - I'd prefer to have the Data class within the test case but gcc 4.x doesn't seem to like it
// An alternate way of doing data tables without structured bindings
struct Data { std::string str; size_t len; };
TEST_CASE( "strlen3" ) {
TEST_CASE( "strlen3", "[generators]" ) {
auto data = GENERATE( values<Data>({
{"one", 3},
{"two", 3},
@ -96,15 +84,7 @@ TEST_CASE( "strlen3" ) {
REQUIRE( data.str.size() == data.len );
}
// A nod towards property-based testing - generate a random selection of numbers
// in a range and assert on global properties those numbers.
static auto square( int i ) -> int { return i*i; }
TEST_CASE( "Random numbers in a range", "[.][approvals]" ) {
auto x = GENERATE( random( -10000, 10000 ) );
CAPTURE( x );
REQUIRE( square(x) >= 0 );
}
#ifdef __cpp_structured_bindings
@ -118,7 +98,7 @@ TEST_CASE( "Random numbers in a range", "[.][approvals]" ) {
static auto eatCucumbers( int start, int eat ) -> int { return start-eat; }
SCENARIO("Eating cucumbers", "[approvals]") {
SCENARIO("Eating cucumbers", "[generators][approvals]") {
auto [start, eat, left] = GENERATE( table<int,int,int> ({
{ 12, 5, 7 },
@ -132,3 +112,36 @@ SCENARIO("Eating cucumbers", "[approvals]") {
}
}
#endif
// There are also some generic generator manipulators
TEST_CASE("Generators -- adapters", "[generators]") {
// TODO: This won't work yet, introduce GENERATE_VAR?
//auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 });
SECTION("Filtering by predicate") {
// This filters out all odd (false) numbers, giving [2, 4, 6]
auto i = GENERATE(filter([] (int val) { return val % 2 == 0; }, values({ 1, 2, 3, 4, 5, 6 })));
REQUIRE(i % 2 == 0);
}
SECTION("Shortening a range") {
// This takes the first 3 elements from the values, giving back [1, 2, 3]
auto i = GENERATE(take(3, values({ 1, 2, 3, 4, 5, 6 })));
REQUIRE(i < 4);
}
SECTION("Transforming elements") {
SECTION("Same type") {
// This doubles values [1, 2, 3] into [2, 4, 6]
auto i = GENERATE(map([] (int val) { return val * 2; }, values({ 1, 2, 3 })));
REQUIRE(i % 2 == 0);
}
SECTION("Different type") {
// This takes a generator that returns ints and maps them into strings
auto i = GENERATE(map<std::string>([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 })));
REQUIRE(i.size() == 1);
}
}
SECTION("Repeating a generator") {
// This will return values [1, 2, 3, 1, 2, 3]
auto j = GENERATE(repeat(2, values({ 1, 2, 3 })));
REQUIRE(j > 0);
}
}