| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | #include "catch.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | #include <cstring>
 | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | // 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); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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()); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | // 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); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | // 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))); | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef __cpp_structured_bindings
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  | // Structured bindings make the table utility much nicer to use
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | TEST_CASE( "strlen2", "[approvals][generators]" ) { | 
					
						
							| 
									
										
										
										
											2018-08-31 15:56:57 +02:00
										 |  |  |     auto [test_input, expected] = GENERATE( table<std::string, size_t>({ | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |             {"one", 3}, | 
					
						
							|  |  |  |             {"two", 3}, | 
					
						
							|  |  |  |             {"three", 5}, | 
					
						
							|  |  |  |             {"four", 4} | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     REQUIRE( test_input.size() == expected ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // An alternate way of doing data tables without structured bindings
 | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | struct Data { std::string str; size_t len; }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | TEST_CASE( "strlen3", "[generators]" ) { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  |     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).
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-31 15:56:57 +02:00
										 |  |  | static auto eatCucumbers( int start, int eat ) -> int { return start-eat; } | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-27 19:46:28 +01:00
										 |  |  | SCENARIO("Eating cucumbers", "[generators][approvals]") { | 
					
						
							| 
									
										
										
										
											2018-06-12 17:50:39 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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
 | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // There are also some generic generator manipulators
 | 
					
						
							| 
									
										
										
										
											2019-02-23 20:22:46 +01:00
										 |  |  | TEST_CASE("Generators -- adapters", "[generators][generic]") { | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  |     // TODO: This won't work yet, introduce GENERATE_VAR?
 | 
					
						
							|  |  |  |     //auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 });
 | 
					
						
							|  |  |  |     SECTION("Filtering by predicate") { | 
					
						
							| 
									
										
										
										
											2019-02-23 20:37:30 +01:00
										 |  |  |         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); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-03-24 09:44:22 -05:00
										 |  |  |         SECTION("Different deduced type") { | 
					
						
							|  |  |  |             // This takes a generator that returns ints and maps them into strings
 | 
					
						
							|  |  |  |             auto i = GENERATE(map([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 }))); | 
					
						
							|  |  |  |             REQUIRE(i.size() == 1); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-02-23 20:22:46 +01:00
										 |  |  |     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); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-06-29 23:48:30 +10:00
										 |  |  |         SECTION("Chunk size of zero") { | 
					
						
							|  |  |  |             auto chunk2 = GENERATE(take(3, chunk(0, value(1)))); | 
					
						
							|  |  |  |             REQUIRE(chunk2.size() == 0); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-02-23 20:22:46 +01:00
										 |  |  |         SECTION("Throws on too small generators") { | 
					
						
							|  |  |  |             using namespace Catch::Generators; | 
					
						
							|  |  |  |             REQUIRE_THROWS_AS(chunk(2, value(1)), Catch::GeneratorException); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-01-27 23:50:43 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-02-19 22:09:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Note that because of the non-reproducibility of distributions,
 | 
					
						
							|  |  |  | // anything involving the random generators cannot be part of approvals
 | 
					
						
							| 
									
										
										
										
											2019-08-09 11:21:05 +02:00
										 |  |  | TEST_CASE("Random generator", "[generators][approvals]") { | 
					
						
							| 
									
										
										
										
											2019-02-19 22:09:14 +01:00
										 |  |  |     SECTION("Infer int from integral arguments") { | 
					
						
							|  |  |  |         auto val = GENERATE(take(4, random(0, 1))); | 
					
						
							|  |  |  |         STATIC_REQUIRE(std::is_same<decltype(val), int>::value); | 
					
						
							| 
									
										
										
										
											2019-08-09 11:21:05 +02:00
										 |  |  |         REQUIRE(0 <= val); | 
					
						
							|  |  |  |         REQUIRE(val <= 1); | 
					
						
							| 
									
										
										
										
											2019-02-19 22:09:14 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     SECTION("Infer double from double arguments") { | 
					
						
							|  |  |  |         auto val = GENERATE(take(4, random(0., 1.))); | 
					
						
							|  |  |  |         STATIC_REQUIRE(std::is_same<decltype(val), double>::value); | 
					
						
							| 
									
										
										
										
											2019-08-09 11:21:05 +02:00
										 |  |  |         REQUIRE(0. <= val); | 
					
						
							|  |  |  |         REQUIRE(val < 1); | 
					
						
							| 
									
										
										
										
											2019-02-19 22:09:14 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-31 14:11:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | TEST_CASE("Nested generators and captured variables", "[generators]") { | 
					
						
							|  |  |  |     // Workaround for old libstdc++
 | 
					
						
							|  |  |  |     using record = std::tuple<int, int>; | 
					
						
							|  |  |  |     // Set up 3 ranges to generate numbers from
 | 
					
						
							|  |  |  |     auto extent = GENERATE(table<int, int>({ | 
					
						
							|  |  |  |         record{3, 7}, | 
					
						
							|  |  |  |         record{-5, -3}, | 
					
						
							|  |  |  |         record{90, 100} | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto from = std::get<0>(extent); | 
					
						
							|  |  |  |     auto to = std::get<1>(extent); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto values = GENERATE_COPY(range(from, to)); | 
					
						
							|  |  |  |     REQUIRE(values > -6); | 
					
						
							|  |  |  | } |