1 #include "catch.hpp" 2 3 #include <cstring> 4 5 6 // Generators and sections can be nested freely 7 TEST_CASE("Generators -- simple", "[generators]") { 8 auto i = GENERATE(1, 2, 3); 9 SECTION("one") { 10 auto j = GENERATE(values({ -3, -2, -1 })); 11 REQUIRE(j < i); 12 } 13 14 SECTION("two") { 15 // You can also explicitly set type for generators via Catch::Generators::as 16 auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc"); 17 REQUIRE(4u * i > str.size()); 18 } 19 } 20 21 // You can create a cartesian-product of generators by creating multiple ones 22 TEST_CASE("3x3x3 ints", "[generators]") { 23 auto x = GENERATE(1, 2, 3); 24 auto y = GENERATE(4, 5, 6); 25 auto z = GENERATE(7, 8, 9); 26 // These assertions will be run 27 times (3x3x3) 27 CHECK(x < y); 28 CHECK(y < z); 29 REQUIRE(x < z); 30 } 31 32 // You can also create data tuples 33 TEST_CASE("tables", "[generators]") { 34 // Note that this will not compile with libstdc++ older than libstdc++6 35 // See https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list 36 // for possible workarounds 37 // auto data = GENERATE(table<char const*, int>({ 38 // {"first", 5}, 39 // {"second", 6}, 40 // {"third", 5}, 41 // {"etc...", 6} 42 // })); 43 44 // Workaround for the libstdc++ bug mentioned above 45 using tuple_type = std::tuple<char const*, int>; 46 auto data = GENERATE(table<char const*, int>({ 47 tuple_type{"first", 5}, 48 tuple_type{"second", 6}, 49 tuple_type{"third", 5}, 50 tuple_type{"etc...", 6} 51 })); 52 53 REQUIRE(strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data))); 54 } 55 56 57 #ifdef __cpp_structured_bindings 58 59 // Structured bindings make the table utility much nicer to use 60 TEST_CASE( "strlen2", "[approvals][generators]" ) { 61 auto [test_input, expected] = GENERATE( table<std::string, size_t>({ 62 {"one", 3}, 63 {"two", 3}, 64 {"three", 5}, 65 {"four", 4} 66 })); 67 68 REQUIRE( test_input.size() == expected ); 69 } 70 #endif 71 72 73 // An alternate way of doing data tables without structured bindings 74 struct Data { std::string str; size_t len; }; 75 76 TEST_CASE( "strlen3", "[generators]" ) { 77 auto data = GENERATE( values<Data>({ 78 {"one", 3}, 79 {"two", 3}, 80 {"three", 5}, 81 {"four", 4} 82 })); 83 84 REQUIRE( data.str.size() == data.len ); 85 } 86 87 88 89 #ifdef __cpp_structured_bindings 90 91 // Based on example from https://docs.cucumber.io/gherkin/reference/#scenario-outline 92 // (thanks to https://github.com/catchorg/Catch2/issues/850#issuecomment-399504851) 93 94 // Note that GIVEN, WHEN, and THEN now forward onto DYNAMIC_SECTION instead of SECTION. 95 // DYNAMIC_SECTION takes its name as a stringstream-style expression, so can be formatted using 96 // variables in scope - such as the generated variables here. This reads quite nicely in the 97 // test name output (the full scenario description). 98 99 static auto eatCucumbers( int start, int eat ) -> int { return start-eat; } 100 101 SCENARIO("Eating cucumbers", "[generators][approvals]") { 102 103 auto [start, eat, left] = GENERATE( table<int,int,int> ({ 104 { 12, 5, 7 }, 105 { 20, 5, 15 } 106 })); 107 108 GIVEN( "there are " << start << " cucumbers" ) 109 WHEN( "I eat " << eat << " cucumbers" ) 110 THEN( "I should have " << left << " cucumbers" ) { 111 REQUIRE( eatCucumbers( start, eat ) == left ); 112 } 113 } 114 #endif 115 116 // There are also some generic generator manipulators 117 TEST_CASE("Generators -- adapters", "[generators][generic]") { 118 // TODO: This won't work yet, introduce GENERATE_VAR? 119 //auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 }); 120 SECTION("Filtering by predicate") { 121 SECTION("Basic usage") { 122 // This filters out all odd (false) numbers, giving [2, 4, 6] 123 auto i = GENERATE(filter([] (int val) { return val % 2 == 0; }, values({ 1, 2, 3, 4, 5, 6 }))); 124 REQUIRE(i % 2 == 0); 125 } 126 SECTION("Throws if there are no matching values") { 127 using namespace Catch::Generators; 128 REQUIRE_THROWS_AS(filter([] (int) {return false; }, value(1)), Catch::GeneratorException); 129 } 130 } 131 SECTION("Shortening a range") { 132 // This takes the first 3 elements from the values, giving back [1, 2, 3] 133 auto i = GENERATE(take(3, values({ 1, 2, 3, 4, 5, 6 }))); 134 REQUIRE(i < 4); 135 } 136 SECTION("Transforming elements") { 137 SECTION("Same type") { 138 // This doubles values [1, 2, 3] into [2, 4, 6] 139 auto i = GENERATE(map([] (int val) { return val * 2; }, values({ 1, 2, 3 }))); 140 REQUIRE(i % 2 == 0); 141 } 142 SECTION("Different type") { 143 // This takes a generator that returns ints and maps them into strings 144 auto i = GENERATE(map<std::string>([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 }))); 145 REQUIRE(i.size() == 1); 146 } 147 } 148 SECTION("Repeating a generator") { 149 // This will return values [1, 2, 3, 1, 2, 3] 150 auto j = GENERATE(repeat(2, values({ 1, 2, 3 }))); 151 REQUIRE(j > 0); 152 } 153 SECTION("Chunking a generator into sized pieces") { 154 SECTION("Number of elements in source is divisible by chunk size") { 155 auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3, 3 }))); 156 REQUIRE(chunk2.size() == 2); 157 REQUIRE(chunk2.front() == chunk2.back()); 158 } 159 SECTION("Number of elements in source is not divisible by chunk size") { 160 auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3 }))); 161 REQUIRE(chunk2.size() == 2); 162 REQUIRE(chunk2.front() == chunk2.back()); 163 REQUIRE(chunk2.front() < 3); 164 } 165 SECTION("Throws on too small generators") { 166 using namespace Catch::Generators; 167 REQUIRE_THROWS_AS(chunk(2, value(1)), Catch::GeneratorException); 168 } 169 } 170 } 171 172 // Note that because of the non-reproducibility of distributions, 173 // anything involving the random generators cannot be part of approvals 174 TEST_CASE("Random generator", "[generators][.][approvals]") { 175 SECTION("Infer int from integral arguments") { 176 auto val = GENERATE(take(4, random(0, 1))); 177 STATIC_REQUIRE(std::is_same<decltype(val), int>::value); 178 static_cast<void>(val); // Silence VS 2015 unused variable warning 179 } 180 SECTION("Infer double from double arguments") { 181 auto val = GENERATE(take(4, random(0., 1.))); 182 STATIC_REQUIRE(std::is_same<decltype(val), double>::value); 183 static_cast<void>(val); // Silence VS 2015 unused variable warning 184 } 185 } 186