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> <a id="top"></a>
# Data Generators # 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_) Data generators (also known as _data driven/parametrized test cases_)
let you reuse the same set of assertions across different input values. let you reuse the same set of assertions across different input values.
In Catch2, this means that they respect the ordering and nesting In Catch2, this means that they respect the ordering and nesting
of the `TEST_CASE` and `SECTION` macros. of the `TEST_CASE` and `SECTION` macros, and their nested sections
are run once per each value in a generator.
How does combining generators and test cases work might be better
explained by an example:
This is best explained with an example:
```cpp ```cpp
TEST_CASE("Generators") { TEST_CASE("Generators") {
auto i = GENERATE( range(1, 11) ); auto i = GENERATE(1, 2, 3);
SECTION("one") {
SECTION( "Some section" ) { auto j = GENERATE( -3, -2, -1 );
auto j = GENERATE( range( 11, 21 ) ); REQUIRE(j < i);
REQUIRE(i < j);
} }
} }
``` ```
the assertion will be checked 100 times, because there are 10 possible The assertion in this test case will be run 9 times, because there
values for `i` (1, 2, ..., 10) and for each of them, there are 10 possible are 3 possible values for `i` (1, 2, and 3) and there are 3 possible
values for `j` (11, 12, ..., 20). 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 ```cpp
static int square(int x) { return x * x; } TEST_CASE("Generating random ints", "[example][generator]") {
TEST_CASE("Generators 2") { SECTION("Deducing functions") {
auto i = GENERATE(0, 1, -1, range(-20, -10), range(10, 20)); auto i = GENERATE(take(100, filter([](int i) { return i % 2 == 1; }, random(-100, 100))));
CAPTURE(i); REQUIRE(i > -100);
REQUIRE(square(i) >= 0); REQUIRE(i < 100);
REQUIRE(i % 2 == 1);
}
} }
``` ```
This will call `square` with arguments `0`, `1`, `-1`, `-20`, ..., `-11`, _Note that `random` is currently not a part of the first-party generators_.
`10`, ..., `19`.
----------
Because of the experimental nature of the current Generator implementation, Apart from registering generators with Catch2, the `GENERATE` macro has
we won't list all of the first-party generators in Catch2. Instead you one more purpose, and that is to provide simple way of generating trivial
should look at our current usage tests in generators, as seen in the first example on this page, where we used it
[projects/SelfTest/UsageTests/Generators.tests.cpp](/projects/SelfTest/UsageTests/Generators.tests.cpp). as `auto i = GENERATE(1, 2, 3);`. This usage converted each of the three
For implementing your own generators, you can look at their implementation in literals into a single `ValueGenerator<int>` and then placed them all in
[include/internal/catch_generators.hpp](/include/internal/catch_generators.hpp). 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) - Report: [TeamCity reporter](../examples/207-Rpt-TeamCityReporter.cpp)
- Listener: [Listeners](../examples/210-Evt-EventListeners.cpp) - Listener: [Listeners](../examples/210-Evt-EventListeners.cpp)
- Configuration: [Provide your own output streams](../examples/231-Cfg-OutputStreams.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 ## 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 110-Fix-ClassFixture.cpp
120-Bdd-ScenarioGivenWhenThen.cpp 120-Bdd-ScenarioGivenWhenThen.cpp
210-Evt-EventListeners.cpp 210-Evt-EventListeners.cpp
300-Gen-OwnGenerator.cpp
310-Gen-VariablesInGenerators.cpp
) )
# main-s for reporter-specific test sources: # main-s for reporter-specific test sources:

View File

@ -16,35 +16,17 @@ namespace Catch {
IGeneratorTracker::~IGeneratorTracker() {} IGeneratorTracker::~IGeneratorTracker() {}
const char* GeneratorException::what() const noexcept {
return m_msg;
}
namespace Generators { namespace Generators {
GeneratorBase::~GeneratorBase() {} GeneratorUntypedBase::~GeneratorUntypedBase() {}
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;
}
auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
return getResultCapture().acquireGeneratorTracker( lineInfo ); 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 Generators
} // namespace Catch } // namespace Catch

View File

@ -16,8 +16,21 @@
#include <cassert> #include <cassert>
#include <utility> #include <utility>
#include <exception>
namespace Catch { 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 { namespace Generators {
// !TBD move this into its own location? // !TBD move this into its own location?
@ -29,202 +42,312 @@ namespace Generators {
} }
template<typename T> template<typename T>
struct IGenerator { struct IGenerator : GeneratorUntypedBase {
virtual ~IGenerator() {} virtual ~IGenerator() = default;
virtual auto get( size_t index ) const -> T = 0;
// 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> template<typename T>
class SingleValueGenerator : public IGenerator<T> { class SingleValueGenerator final : public IGenerator<T> {
T m_value; T m_value;
public: 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; return m_value;
} }
bool next() override {
return false;
}
}; };
template<typename T> template<typename T>
class FixedValuesGenerator : public IGenerator<T> { class FixedValuesGenerator final : public IGenerator<T> {
std::vector<T> m_values; std::vector<T> m_values;
size_t m_idx = 0;
public: public:
FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {} FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
auto get( size_t index ) const -> T override { T const& get() const override {
return m_values[index]; return m_values[m_idx];
}
bool next() override {
++m_idx;
return m_idx < m_values.size();
} }
}; };
template<typename T> template <typename T>
class RangeGenerator : public IGenerator<T> { class GeneratorWrapper final {
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 {
std::unique_ptr<IGenerator<T>> m_generator; std::unique_ptr<IGenerator<T>> m_generator;
size_t m_size;
public: public:
Generator( size_t size, std::unique_ptr<IGenerator<T>> generator ) GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
: m_generator( std::move( generator ) ), m_generator(std::move(generator))
m_size( size )
{} {}
T const& get() const {
auto size() const -> size_t { return m_size; } return m_generator->get();
auto operator[]( size_t index ) const -> T { }
assert( index < m_size ); bool next() {
return m_generator->get( index ); 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> template<typename T>
class GeneratorRandomiser : public IGenerator<T> { class Generators : public IGenerator<T> {
Generator<T> m_baseGenerator; std::vector<GeneratorWrapper<T>> m_generators;
size_t m_current = 0;
std::vector<size_t> m_indices; void populate(GeneratorWrapper<T>&& generator) {
public: m_generators.emplace_back(std::move(generator));
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(T&& val) {
m_generators.emplace_back(value(std::move(val)));
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 ) ) );
} }
template<typename U> template<typename U>
void populate( U&& val ) { void populate(U&& val) {
populate( T( std::move( 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> template<typename U, typename... Gs>
void populate( U&& valueOrGenerator, Gs... moreGenerators ) { void populate(U&& valueOrGenerator, Gs... moreGenerators) {
populate( std::forward<U>( valueOrGenerator ) ); populate(std::forward<U>(valueOrGenerator));
populate( std::forward<Gs>( moreGenerators )... ); populate(std::forward<Gs>(moreGenerators)...);
} }
auto operator[]( size_t index ) const -> T { public:
size_t sizes = 0; template <typename... Gs>
for( auto const& gen : m_generators ) { Generators(Gs... moreGenerators) {
auto localIndex = index-sizes; m_generators.reserve(sizeof...(Gs));
sizes += gen.size(); populate(std::forward<Gs>(moreGenerators)...);
if( index < sizes ) }
return gen[localIndex];
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> template<typename T, typename... Gs>
auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> { auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
Generators<T> generators; return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
generators.m_generators.reserve( 1+sizeof...(Gs) );
generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... );
return generators;
} }
template<typename T> template<typename T>
auto makeGenerators( Generator<T>&& generator ) -> Generators<T> { auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
Generators<T> generators; return Generators<T>(std::move(generator));
generators.populate( std::move( generator ) );
return generators;
} }
template<typename T, typename... Gs> template<typename T, typename... Gs>
auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> { auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... ); return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
} }
template<typename T, typename U, typename... Gs> 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 )... ); 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&; auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
@ -232,15 +355,16 @@ namespace Generators {
// Note: The type after -> is weird, because VS2015 cannot parse // Note: The type after -> is weird, because VS2015 cannot parse
// the expression used in the typedef inside, when it is in // the expression used in the typedef inside, when it is in
// return type. Yeah, ¯\_(ツ)_/¯ // 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; using UnderlyingType = typename decltype(generatorExpression())::type;
IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
if( !tracker.hasGenerator() ) if (!tracker.hasGenerator()) {
tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) ); tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
}
auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() ); auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
return generator[tracker.getIndex()]; return generator.get();
} }
} // namespace Generators } // namespace Generators

View File

@ -13,16 +13,17 @@
namespace Catch { namespace Catch {
namespace Generators { namespace Generators {
class GeneratorBase { class GeneratorUntypedBase {
protected:
size_t m_size = 0;
public: public:
GeneratorBase( size_t size ) : m_size( size ) {} GeneratorUntypedBase() = default;
virtual ~GeneratorBase(); virtual ~GeneratorUntypedBase();
auto size() const -> size_t { return m_size; } // 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 } // namespace Generators
@ -31,7 +32,6 @@ namespace Catch {
virtual auto hasGenerator() const -> bool = 0; virtual auto hasGenerator() const -> bool = 0;
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0; virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0; virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
virtual auto getIndex() const -> std::size_t = 0;
}; };
} // namespace Catch } // namespace Catch

View File

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

View File

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

View File

@ -54,7 +54,7 @@ namespace TestCaseTracking {
// Debug/ checking // Debug/ checking
virtual bool isSectionTracker() const = 0; virtual bool isSectionTracker() const = 0;
virtual bool isIndexTracker() const = 0; virtual bool isGeneratorTracker() const = 0;
}; };
class TrackerContext { class TrackerContext {
@ -120,7 +120,7 @@ namespace TestCaseTracking {
void openChild() override; void openChild() override;
bool isSectionTracker() const override; bool isSectionTracker() const override;
bool isIndexTracker() const override; bool isGeneratorTracker() const override;
void open(); 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<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 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 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 102 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 103 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 104 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 105 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 106 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 107 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 108 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 109 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 1 < 110 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 101 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 102 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 103 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 104 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 105 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 106 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 107 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 108 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 109 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 2 < 110 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 101 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 102 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 103 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 104 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 105 Generators.tests.cpp:<line number>: passed: x < y for: 1 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 106 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 107 Generators.tests.cpp:<line number>: passed: x < z for: 1 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 108 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 109 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 3 < 110 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 101 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 102 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 103 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 104 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 105 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 106 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 107 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 108 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 109 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 4 < 110 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 101 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 102 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 103 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 104 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 105 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 106 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 107 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 108 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 109 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 5 < 110 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 101 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 102 Generators.tests.cpp:<line number>: passed: x < y for: 2 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 103 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 104 Generators.tests.cpp:<line number>: passed: x < z for: 2 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 105 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 106 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 107 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 108 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 109 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 6 < 110 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 101 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 4
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 102 Generators.tests.cpp:<line number>: passed: y < z for: 4 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 103 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 104 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 105 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 106 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 107 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 108 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 109 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 7 < 110 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 5
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 101 Generators.tests.cpp:<line number>: passed: y < z for: 5 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 102 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 103 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 104 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 105 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 7
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 106 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 107 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 108 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 8
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 109 Generators.tests.cpp:<line number>: passed: x < y for: 3 < 6
Generators.tests.cpp:<line number>: passed: x < y for: 8 < 110 Generators.tests.cpp:<line number>: passed: y < z for: 6 < 9
Generators.tests.cpp:<line number>: passed: x < y for: 9 < 101 Generators.tests.cpp:<line number>: passed: x < z for: 3 < 9
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
Class.tests.cpp:<line number>: failed: s == "world" for: "hello" == "world" 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>: passed: s == "hello" for: "hello" == "hello"
Class.tests.cpp:<line number>: failed: Template_Fixture_2<TestType>::m_a.size() == 1 for: 0 == 1 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: 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, 0)
Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, -1), std::domain_error 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: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 9' Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 10' Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 2' Generators.tests.cpp:<line number>: passed: i < 4 for: 1 < 4
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 3.141' Generators.tests.cpp:<line number>: passed: i < 4 for: 2 < 4
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "a"' and 'j := 1.379' Generators.tests.cpp:<line number>: passed: i < 4 for: 3 < 4
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 8' Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 9' Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 10' Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 2' Generators.tests.cpp:<line number>: passed: i.size() == 1 for: 1 == 1
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 3.141' Generators.tests.cpp:<line number>: passed: i.size() == 1 for: 1 == 1
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "b"' and 'j := 1.379' Generators.tests.cpp:<line number>: passed: i.size() == 1 for: 1 == 1
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 8' Generators.tests.cpp:<line number>: passed: j > 0 for: 1 > 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 9' Generators.tests.cpp:<line number>: passed: j > 0 for: 2 > 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 10' Generators.tests.cpp:<line number>: passed: j > 0 for: 3 > 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 2' Generators.tests.cpp:<line number>: passed: j > 0 for: 1 > 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 3.141' Generators.tests.cpp:<line number>: passed: j > 0 for: 2 > 0
Generators.tests.cpp:<line number>: passed: with 2 messages: 'i := "c"' and 'j := 1.379' Generators.tests.cpp:<line number>: passed: j > 0 for: 3 > 0
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 2 for: 2 == 2 Generators.tests.cpp:<line number>: passed: j < i for: -3 < 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 1 for: 1 == 1 Generators.tests.cpp:<line number>: passed: j < i for: -2 < 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 2 for: 2 == 2 Generators.tests.cpp:<line number>: passed: j < i for: -1 < 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 4 for: 4 == 4 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 4 > 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 3 for: 3 == 3 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 4 > 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 1 for: 1 == 1 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 4 > 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[2] == 4 for: 4 == 4 Generators.tests.cpp:<line number>: passed: j < i for: -3 < 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[3] == 1 for: 1 == 1 Generators.tests.cpp:<line number>: passed: j < i for: -2 < 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 4 for: 4 == 4 Generators.tests.cpp:<line number>: passed: j < i for: -1 < 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 1 for: 1 == 1 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 8 > 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 2 for: 2 == 2 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 8 > 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[2] == 9 for: 9 == 9 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 8 > 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[3] == 7 for: 7 == 7 Generators.tests.cpp:<line number>: passed: j < i for: -3 < 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 2 for: 2 == 2 Generators.tests.cpp:<line number>: passed: j < i for: -2 < 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 3 for: 3 == 3 Generators.tests.cpp:<line number>: passed: j < i for: -1 < 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 1 for: 1 == 1 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 1
GeneratorsImpl.tests.cpp:<line number>: passed: gen.size() == 2 for: 2 == 2 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 2
GeneratorsImpl.tests.cpp:<line number>: passed: gen[0] == 3 for: 3 == 3 Generators.tests.cpp:<line number>: passed: 4u * i > str.size() for: 12 > 3
GeneratorsImpl.tests.cpp:<line number>: passed: gen[1] == 1 for: 1 == 1 GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 123 for: 123 == 123
GeneratorsImpl.tests.cpp:<line number>: passed: base->size() == 4 for: 4 == 4 GeneratorsImpl.tests.cpp:<line number>: passed: !(gen.next()) for: !false
GeneratorsImpl.tests.cpp:<line number>: passed: typed for: 0x<hex digits> GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 1 for: 1 == 1
GeneratorsImpl.tests.cpp:<line number>: passed: typed->size() == 4 for: 4 == 4 GeneratorsImpl.tests.cpp:<line number>: passed: gen.next() for: true
GeneratorsImpl.tests.cpp:<line number>: passed: (*typed)[0] == 7 for: 7 == 7 GeneratorsImpl.tests.cpp:<line number>: passed: gen.get() == 3 for: 3 == 3
GeneratorsImpl.tests.cpp:<line number>: passed: (*typed)[3] == 11 for: 11 == 11 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.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.23 ) for: 1.23 >= Approx( 1.23 )
Approx.tests.cpp:<line number>: passed: !(d >= Approx( 1.24 )) for: !(1.23 >= Approx( 1.24 )) 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: 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: 5 == 5
Generators.tests.cpp:<line number>: passed: data.str.size() == data.len for: 4 == 4 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?' 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""
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? Why would you throw a std::string?
=============================================================================== ===============================================================================
test cases: 243 | 183 passed | 56 failed | 4 failed as expected test cases: 245 | 185 passed | 56 failed | 4 failed as expected
assertions: 1262 | 1126 passed | 115 failed | 21 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"?> <?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact <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="# 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="#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}"/> <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/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/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="(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}"> <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"> <failure message="&quot;hello&quot; == &quot;world&quot;" type="REQUIRE">
Class.tests.cpp:<line number> 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/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/Composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Constructor validation" 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 -- adapters/Filtering by predicate" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators/two" time="{duration}"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Shortening a range" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/range" time="{duration}"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Transforming elements/Same type" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/fixed values" time="{duration}"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Transforming elements/Different type" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/combined" time="{duration}"/> <testcase classname="<exe-name>.global" name="Generators -- adapters/Repeating a generator" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/values" time="{duration}"/> <testcase classname="<exe-name>.global" name="Generators -- simple/one" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/values2" time="{duration}"/> <testcase classname="<exe-name>.global" name="Generators -- simple/two" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Generators impl/type erasure" 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="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 and WARN do not abort tests" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO gets logged on failure" 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_maker_and_operator> )" time="{duration}"/>
<testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_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="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}"> <testcase classname="<exe-name>.global" name="thrown std::strings are translated" time="{duration}">
<error type="TEST_CASE"> <error type="TEST_CASE">
Why would you throw a std::string? 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" #include "catch.hpp"
// Tests of generartor implementation details // Tests of generator implementation details
TEST_CASE("Generators internals", "[generators][internals]") {
TEST_CASE("Generators impl", "[impl]") {
using namespace Catch::Generators; using namespace Catch::Generators;
SECTION( "range" ) { SECTION("Single value") {
auto gen = range(1,3); auto gen = value(123);
REQUIRE(gen.get() == 123);
CHECK( gen.size() == 2 ); REQUIRE_FALSE(gen.next());
CHECK( gen[0] == 1 );
CHECK( gen[1] == 2 );
} }
SECTION( "fixed values" ) { SECTION("Preset values") {
auto gen = values( { 3, 1, 4, 1 } ); auto gen = values({ 1, 3, 5 });
REQUIRE(gen.get() == 1);
CHECK( gen.size() == 4 ); REQUIRE(gen.next());
CHECK( gen[0] == 3 ); REQUIRE(gen.get() == 3);
CHECK( gen[1] == 1 ); REQUIRE(gen.next());
CHECK( gen[2] == 4 ); REQUIRE(gen.get() == 5);
CHECK( gen[3] == 1 ); REQUIRE_FALSE(gen.next());
} }
SECTION( "combined" ) { SECTION("Generator combinator") {
auto gen = makeGenerators( range( 1, 3 ), values( { 9, 7 } ) ); auto gen = makeGenerators(1, 5, values({ 2, 4 }), 0);
REQUIRE(gen.get() == 1);
CHECK( gen.size() == 4 ); REQUIRE(gen.next());
CHECK( gen[0] == 1 ); REQUIRE(gen.get() == 5);
CHECK( gen[1] == 2 ); REQUIRE(gen.next());
CHECK( gen[2] == 9 ); REQUIRE(gen.get() == 2);
CHECK( gen[3] == 7 ); REQUIRE(gen.next());
REQUIRE(gen.get() == 4);
REQUIRE(gen.next());
REQUIRE(gen.get() == 0);
REQUIRE_FALSE(gen.next());
} }
SECTION("Explicitly typed generator sequence") {
SECTION( "values" ) { auto gen = makeGenerators(as<std::string>{}, "aa", "bb", "cc");
auto gen = makeGenerators( 3, 1 ); // This just checks that the type is std::string:
REQUIRE(gen.get().size() == 2);
CHECK( gen.size() == 2 ); // Iterate over the generator
CHECK( gen[0] == 3 ); REQUIRE(gen.get() == "aa");
CHECK( gen[1] == 1 ); REQUIRE(gen.next());
REQUIRE(gen.get() == "bb");
REQUIRE(gen.next());
REQUIRE(gen.get() == "cc");
REQUIRE_FALSE(gen.next());
} }
SECTION( "values2" ) { SECTION("Filter generator") {
auto gen = makeGenerators( 3, 1 ); // 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 ); // Completely filtered-out generator should throw on construction
CHECK( gen[0] == 3 ); REQUIRE_THROWS_AS(filter([] (int) { return false; }, value(1)), Catch::GeneratorException);
CHECK( gen[1] == 1 );
} }
SECTION("Take generator") {
SECTION("Take less") {
SECTION( "type erasure" ) { auto gen = take(2, values({ 1, 2, 3 }));
auto gen = makeGenerators( range( 7, 10 ), 11 ); REQUIRE(gen.get() == 1);
REQUIRE(gen.next());
// Make type erased version REQUIRE(gen.get() == 2);
auto dynCopy = pf::make_unique<Generators<int>>( std::move( gen ) ); REQUIRE_FALSE(gen.next());
std::unique_ptr<GeneratorBase const> base = std::move( dynCopy ); }
SECTION("Take more") {
// Only thing we can do is ask for the size auto gen = take(2, value(1));
CHECK( base->size() == 4 ); REQUIRE(gen.get() == 1);
REQUIRE_FALSE(gen.next());
// 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( "random selection" ) { SECTION("Map") {
auto gen = random<int>( 10 ); auto gen = map<double>([] (int i) {return 2.0 * i; }, values({ 1, 2, 3 }));
REQUIRE(gen.get() == 2.0);
CHECK( gen.size() == 10 ); REQUIRE(gen.next());
for( size_t i = 0; i < 10; ++i ) { REQUIRE(gen.get() == 4.0);
if( i > 0 ) REQUIRE(gen.next());
CHECK( gen[i] != gen[i-1] ); 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" #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" ); // Generators and sections can be nested freely
TEST_CASE("Generators -- simple", "[generators]") {
SECTION( "one" ) { auto i = GENERATE(1, 2, 3);
auto j = GENERATE( range( 8, 11 ), 2 ); SECTION("one") {
auto j = GENERATE(values({ -3, -2, -1 }));
CAPTURE( i, j ); REQUIRE(j < i);
SUCCEED();
} }
SECTION( "two" ) {
auto j = GENERATE( 3.141, 1.379 ); SECTION("two") {
CAPTURE( i, j ); // You can also explicitly set type for generators via Catch::Generators::as
SUCCEED(); auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");
REQUIRE(4u * i > str.size());
} }
} }
// This one generates the cross-product of two ranges. // You can create a cartesian-product of generators by creating multiple ones
// It's mostly here to demonstrate the performance which, at time of writing, TEST_CASE("3x3x3 ints", "[generators]") {
// leaves a lot to be desired. auto x = GENERATE(1, 2, 3);
TEST_CASE( "100x100 ints", "[.][approvals]" ) { auto y = GENERATE(4, 5, 6);
auto x = GENERATE( range( 0,100 ) ); auto z = GENERATE(7, 8, 9);
auto y = GENERATE( range( 200,300 ) ); // These assertions will be run 27 times (3x3x3)
CHECK(x < y);
CHECK( x < y ); CHECK(y < z);
REQUIRE(x < z);
} }
// smaller version // You can also create data tuples
TEST_CASE( "10x10 ints" ) { TEST_CASE("tables", "[generators]") {
auto x = GENERATE( range( 1,11 ) ); // Note that this will not compile with libstdc++ older than libstdc++6
auto y = GENERATE( range( 101, 111 ) ); // 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 #ifdef __cpp_structured_bindings
// One way to do pairs of values (actual/ expected?) // Structured bindings make the table utility much nicer to use
// For a simple case like this I'd recommend writing out a series of REQUIREs TEST_CASE( "strlen2", "[approvals][generators]" ) {
// 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]" ) {
auto [test_input, expected] = GENERATE( table<std::string, size_t>({ auto [test_input, expected] = GENERATE( table<std::string, size_t>({
{"one", 3}, {"one", 3},
{"two", 3}, {"two", 3},
@ -81,11 +69,11 @@ TEST_CASE( "strlen2", "[approvals]" ) {
} }
#endif #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; }; struct Data { std::string str; size_t len; };
TEST_CASE( "strlen3" ) { TEST_CASE( "strlen3", "[generators]" ) {
auto data = GENERATE( values<Data>({ auto data = GENERATE( values<Data>({
{"one", 3}, {"one", 3},
{"two", 3}, {"two", 3},
@ -96,15 +84,7 @@ TEST_CASE( "strlen3" ) {
REQUIRE( data.str.size() == data.len ); 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 #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; } 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> ({ auto [start, eat, left] = GENERATE( table<int,int,int> ({
{ 12, 5, 7 }, { 12, 5, 7 },
@ -132,3 +112,36 @@ SCENARIO("Eating cucumbers", "[approvals]") {
} }
} }
#endif #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);
}
}