mirror of https://github.com/catchorg/Catch2.git
Merge pull request #1516 from catchorg/dev-generators-take2
This replaces the old interface with a final one.
This commit is contained in:
commit
63d1a96908
|
@ -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).
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
template<typename T>
|
return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
|
||||||
class GeneratorRandomiser : public IGenerator<T> {
|
|
||||||
Generator<T> m_baseGenerator;
|
|
||||||
|
|
||||||
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]];
|
|
||||||
}
|
}
|
||||||
};
|
template <typename T>
|
||||||
|
GeneratorWrapper<T> values(std::initializer_list<T> values) {
|
||||||
template<typename T>
|
return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
|
||||||
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>
|
template<typename T>
|
||||||
auto random( T const& first, T const& last ) -> Generator<T> {
|
class Generators : public IGenerator<T> {
|
||||||
auto gen = range( first, last );
|
std::vector<GeneratorWrapper<T>> m_generators;
|
||||||
auto size = gen.size();
|
size_t m_current = 0;
|
||||||
|
|
||||||
return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) );
|
void populate(GeneratorWrapper<T>&& generator) {
|
||||||
|
m_generators.emplace_back(std::move(generator));
|
||||||
}
|
}
|
||||||
template<typename T>
|
void populate(T&& val) {
|
||||||
auto random( size_t size ) -> Generator<T> {
|
m_generators.emplace_back(value(std::move(val)));
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
|
|
||||||
|
T const& get() const override {
|
||||||
|
return m_generators[m_current].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool next() override {
|
||||||
|
if (m_current >= m_generators.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,29 +36,25 @@ 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
|
||||||
auto getGenerator() const -> GeneratorBasePtr const& override {
|
auto getGenerator() const -> GeneratorBasePtr const& override {
|
||||||
|
@ -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() {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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""
|
||||||
|
|
|
@ -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
|
@ -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=""hello" == "world"" type="REQUIRE">
|
<failure message=""hello" == "world"" 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<has_maker_and_operator> )" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="stringify( vectors<has_maker_and_operator> )" time="{duration}"/>
|
||||||
<testcase classname="<exe-name>.global" name="stringify( vectors<has_operator> )" time="{duration}"/>
|
<testcase classname="<exe-name>.global" name="stringify( vectors<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
|
@ -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);
|
||||||
|
REQUIRE(gen.next());
|
||||||
|
REQUIRE(gen.get() == 5);
|
||||||
|
REQUIRE(gen.next());
|
||||||
|
REQUIRE(gen.get() == 2);
|
||||||
|
REQUIRE(gen.next());
|
||||||
|
REQUIRE(gen.get() == 4);
|
||||||
|
REQUIRE(gen.next());
|
||||||
|
REQUIRE(gen.get() == 0);
|
||||||
|
REQUIRE_FALSE(gen.next());
|
||||||
|
}
|
||||||
|
SECTION("Explicitly typed generator sequence") {
|
||||||
|
auto gen = makeGenerators(as<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("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() == 4 );
|
// Completely filtered-out generator should throw on construction
|
||||||
CHECK( gen[0] == 1 );
|
REQUIRE_THROWS_AS(filter([] (int) { return false; }, value(1)), Catch::GeneratorException);
|
||||||
CHECK( gen[1] == 2 );
|
}
|
||||||
CHECK( gen[2] == 9 );
|
SECTION("Take generator") {
|
||||||
CHECK( gen[3] == 7 );
|
SECTION("Take less") {
|
||||||
|
auto gen = take(2, values({ 1, 2, 3 }));
|
||||||
|
REQUIRE(gen.get() == 1);
|
||||||
|
REQUIRE(gen.next());
|
||||||
|
REQUIRE(gen.get() == 2);
|
||||||
|
REQUIRE_FALSE(gen.next());
|
||||||
|
}
|
||||||
|
SECTION("Take more") {
|
||||||
|
auto gen = take(2, value(1));
|
||||||
|
REQUIRE(gen.get() == 1);
|
||||||
|
REQUIRE_FALSE(gen.next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("Map") {
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION( "values" ) {
|
|
||||||
auto gen = makeGenerators( 3, 1 );
|
|
||||||
|
|
||||||
CHECK( gen.size() == 2 );
|
|
||||||
CHECK( gen[0] == 3 );
|
|
||||||
CHECK( gen[1] == 1 );
|
|
||||||
}
|
|
||||||
SECTION( "values2" ) {
|
|
||||||
auto gen = makeGenerators( 3, 1 );
|
|
||||||
|
|
||||||
CHECK( gen.size() == 2 );
|
|
||||||
CHECK( gen[0] == 3 );
|
|
||||||
CHECK( gen[1] == 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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( "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] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue