Redo generator interface

This commit is contained in:
Martin Hořeňovský
2019-01-27 19:46:28 +01:00
parent 64a9c02315
commit e46a70f829
13 changed files with 1284 additions and 2014 deletions

View File

@@ -1,53 +1,58 @@
#include "catch.hpp"
// Examples of usage of Generators
#include <cstring>
// This test doesn't do much - it just shows how you can have several generators, of different
// types (ie `i` and `j` are different types), can be sequenced using `,` and
// can be expressed as named generators (like range) or as individual values.
// Generators can be mixed with SECTIONs.
// At time of writing the generated values are not automatically reported as part of the test
// name or associated values - so we explicitly CAPTURE then (run this with `-s` to see them).
// We could also incorporate them into the section names using DYNAMIC_SECTION. See the BDD
// example later for more information.
TEST_CASE("Generators") {
auto i = GENERATE( as<std::string>(), "a", "b", "c" );
SECTION( "one" ) {
auto j = GENERATE( range( 8, 11 ), 2 );
CAPTURE( i, j );
SUCCEED();
// Generators and sections can be nested freely
TEST_CASE("Generators -- simple", "[generators]") {
auto i = GENERATE(1, 2, 3);
SECTION("one") {
auto j = GENERATE(values({ -3, -2, -1 }));
REQUIRE(j < i);
}
SECTION( "two" ) {
auto j = GENERATE( 3.141, 1.379 );
CAPTURE( i, j );
SUCCEED();
SECTION("two") {
// You can also explicitly set type for generators via Catch::Generators::as
auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");
REQUIRE(4u * i > str.size());
}
}
// This one generates the cross-product of two ranges.
// It's mostly here to demonstrate the performance which, at time of writing,
// leaves a lot to be desired.
TEST_CASE( "100x100 ints", "[.][approvals]" ) {
auto x = GENERATE( range( 0,100 ) );
auto y = GENERATE( range( 200,300 ) );
CHECK( x < y );
// You can create a cartesian-product of generators by creating multiple ones
TEST_CASE("3x3x3 ints", "[generators]") {
auto x = GENERATE(1, 2, 3);
auto y = GENERATE(4, 5, 6);
auto z = GENERATE(7, 8, 9);
// These assertions will be run 27 times (3x3x3)
CHECK(x < y);
CHECK(y < z);
REQUIRE(x < z);
}
// smaller version
TEST_CASE( "10x10 ints" ) {
auto x = GENERATE( range( 1,11 ) );
auto y = GENERATE( range( 101, 111 ) );
// You can also create data tuples
TEST_CASE("tables", "[generators]") {
// Note that this will not compile with libstdc++ older than libstdc++6
// See https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
// for possible workarounds
// auto data = GENERATE(table<char const*, int>({
// {"first", 5},
// {"second", 6},
// {"third", 5},
// {"etc...", 6}
// }));
CHECK( x < y );
// Workaround for the libstdc++ bug mentioned above
using tuple_type = std::tuple<char const*, int>;
auto data = GENERATE(table<char const*, int>({
tuple_type{"first", 5},
tuple_type{"second", 6},
tuple_type{"third", 5},
tuple_type{"etc...", 6}
}));
REQUIRE(strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)));
}
// Some of the following tests use structured bindings for convenience and so are
// conditionally compiled using the de-facto (and soon to be formally) standard
// feature macros
#ifdef __cpp_structured_bindings
@@ -56,7 +61,7 @@ TEST_CASE( "10x10 ints" ) {
// 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]" ) {
TEST_CASE( "strlen", "[approvals][generators]" ) {
auto [test_input, expected] = GENERATE( values<std::pair<std::string_view, size_t>>({
{"one", 3},
{"two", 3},
@@ -69,7 +74,7 @@ TEST_CASE( "strlen", "[approvals]" ) {
// 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]" ) {
TEST_CASE( "strlen2", "[approvals][generators]" ) {
auto [test_input, expected] = GENERATE( table<std::string, size_t>({
{"one", 3},
{"two", 3},
@@ -81,11 +86,11 @@ TEST_CASE( "strlen2", "[approvals]" ) {
}
#endif
// An alternate way of doing data tables without structure bindings
// - I'd prefer to have the Data class within the test case but gcc 4.x doesn't seem to like it
// An alternate way of doing data tables without structured bindings
struct Data { std::string str; size_t len; };
TEST_CASE( "strlen3" ) {
TEST_CASE( "strlen3", "[generators]" ) {
auto data = GENERATE( values<Data>({
{"one", 3},
{"two", 3},
@@ -96,15 +101,7 @@ TEST_CASE( "strlen3" ) {
REQUIRE( data.str.size() == data.len );
}
// A nod towards property-based testing - generate a random selection of numbers
// in a range and assert on global properties those numbers.
static auto square( int i ) -> int { return i*i; }
TEST_CASE( "Random numbers in a range", "[.][approvals]" ) {
auto x = GENERATE( random( -10000, 10000 ) );
CAPTURE( x );
REQUIRE( square(x) >= 0 );
}
#ifdef __cpp_structured_bindings
@@ -118,7 +115,7 @@ TEST_CASE( "Random numbers in a range", "[.][approvals]" ) {
static auto eatCucumbers( int start, int eat ) -> int { return start-eat; }
SCENARIO("Eating cucumbers", "[approvals]") {
SCENARIO("Eating cucumbers", "[generators][approvals]") {
auto [start, eat, left] = GENERATE( table<int,int,int> ({
{ 12, 5, 7 },