| #include "catch.hpp" |
| |
| #include <cstring> |
| |
| |
| // 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") { |
| // 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()); |
| } |
| } |
| |
| // 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); |
| } |
| |
| // 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} |
| // })); |
| |
| // 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))); |
| } |
| |
| |
| #ifdef __cpp_structured_bindings |
| |
| // Structured bindings make the table utility much nicer to use |
| TEST_CASE( "strlen2", "[approvals][generators]" ) { |
| auto [test_input, expected] = GENERATE( table<std::string, size_t>({ |
| {"one", 3}, |
| {"two", 3}, |
| {"three", 5}, |
| {"four", 4} |
| })); |
| |
| REQUIRE( test_input.size() == expected ); |
| } |
| #endif |
| |
| |
| // An alternate way of doing data tables without structured bindings |
| struct Data { std::string str; size_t len; }; |
| |
| TEST_CASE( "strlen3", "[generators]" ) { |
| auto data = GENERATE( values<Data>({ |
| {"one", 3}, |
| {"two", 3}, |
| {"three", 5}, |
| {"four", 4} |
| })); |
| |
| REQUIRE( data.str.size() == data.len ); |
| } |
| |
| |
| |
| #ifdef __cpp_structured_bindings |
| |
| // Based on example from https://docs.cucumber.io/gherkin/reference/#scenario-outline |
| // (thanks to https://github.com/catchorg/Catch2/issues/850#issuecomment-399504851) |
| |
| // Note that GIVEN, WHEN, and THEN now forward onto DYNAMIC_SECTION instead of SECTION. |
| // DYNAMIC_SECTION takes its name as a stringstream-style expression, so can be formatted using |
| // variables in scope - such as the generated variables here. This reads quite nicely in the |
| // test name output (the full scenario description). |
| |
| static auto eatCucumbers( int start, int eat ) -> int { return start-eat; } |
| |
| SCENARIO("Eating cucumbers", "[generators][approvals]") { |
| |
| auto [start, eat, left] = GENERATE( table<int,int,int> ({ |
| { 12, 5, 7 }, |
| { 20, 5, 15 } |
| })); |
| |
| GIVEN( "there are " << start << " cucumbers" ) |
| WHEN( "I eat " << eat << " cucumbers" ) |
| THEN( "I should have " << left << " cucumbers" ) { |
| REQUIRE( eatCucumbers( start, eat ) == left ); |
| } |
| } |
| #endif |
| |
| // There are also some generic generator manipulators |
| TEST_CASE("Generators -- adapters", "[generators][generic]") { |
| // TODO: This won't work yet, introduce GENERATE_VAR? |
| //auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 }); |
| SECTION("Filtering by predicate") { |
| SECTION("Basic usage") { |
| // 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("Throws if there are no matching values") { |
| using namespace Catch::Generators; |
| REQUIRE_THROWS_AS(filter([] (int) {return false; }, value(1)), Catch::GeneratorException); |
| } |
| } |
| 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); |
| } |
| SECTION("Chunking a generator into sized pieces") { |
| SECTION("Number of elements in source is divisible by chunk size") { |
| auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3, 3 }))); |
| REQUIRE(chunk2.size() == 2); |
| REQUIRE(chunk2.front() == chunk2.back()); |
| } |
| SECTION("Number of elements in source is not divisible by chunk size") { |
| auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3 }))); |
| REQUIRE(chunk2.size() == 2); |
| REQUIRE(chunk2.front() == chunk2.back()); |
| REQUIRE(chunk2.front() < 3); |
| } |
| SECTION("Throws on too small generators") { |
| using namespace Catch::Generators; |
| REQUIRE_THROWS_AS(chunk(2, value(1)), Catch::GeneratorException); |
| } |
| } |
| } |
| |
| // Note that because of the non-reproducibility of distributions, |
| // anything involving the random generators cannot be part of approvals |
| TEST_CASE("Random generator", "[generators][.][approvals]") { |
| SECTION("Infer int from integral arguments") { |
| auto val = GENERATE(take(4, random(0, 1))); |
| STATIC_REQUIRE(std::is_same<decltype(val), int>::value); |
| static_cast<void>(val); // Silence VS 2015 unused variable warning |
| } |
| SECTION("Infer double from double arguments") { |
| auto val = GENERATE(take(4, random(0., 1.))); |
| STATIC_REQUIRE(std::is_same<decltype(val), double>::value); |
| static_cast<void>(val); // Silence VS 2015 unused variable warning |
| } |
| } |