Generators.tests.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. // Copyright Catch2 Authors
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // https://www.boost.org/LICENSE_1_0.txt)
  5. // SPDX-License-Identifier: BSL-1.0
  6. #include <catch2/catch_test_macros.hpp>
  7. #include <catch2/generators/catch_generator_exception.hpp>
  8. #include <catch2/generators/catch_generators_adapters.hpp>
  9. #include <catch2/generators/catch_generators_random.hpp>
  10. #include <catch2/generators/catch_generators_range.hpp>
  11. #include <cstring>
  12. // Generators and sections can be nested freely
  13. TEST_CASE("Generators -- simple", "[generators]") {
  14. auto i = GENERATE(1, 2, 3);
  15. SECTION("one") {
  16. auto j = GENERATE(values({ -3, -2, -1 }));
  17. REQUIRE(j < i);
  18. }
  19. SECTION("two") {
  20. // You can also explicitly set type for generators via Catch::Generators::as
  21. auto str = GENERATE(as<std::string>{}, "a", "bb", "ccc");
  22. REQUIRE(4u * i > str.size());
  23. }
  24. }
  25. // You can create a cartesian-product of generators by creating multiple ones
  26. TEST_CASE("3x3x3 ints", "[generators]") {
  27. auto x = GENERATE(1, 2, 3);
  28. auto y = GENERATE(4, 5, 6);
  29. auto z = GENERATE(7, 8, 9);
  30. // These assertions will be run 27 times (3x3x3)
  31. CHECK(x < y);
  32. CHECK(y < z);
  33. REQUIRE(x < z);
  34. }
  35. // You can also create data tuples
  36. TEST_CASE("tables", "[generators]") {
  37. // Note that this will not compile with libstdc++ older than libstdc++6
  38. // See https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
  39. // for possible workarounds
  40. // auto data = GENERATE(table<char const*, int>({
  41. // {"first", 5},
  42. // {"second", 6},
  43. // {"third", 5},
  44. // {"etc...", 6}
  45. // }));
  46. // Workaround for the libstdc++ bug mentioned above
  47. using tuple_type = std::tuple<char const*, int>;
  48. auto data = GENERATE(table<char const*, int>({
  49. tuple_type{"first", 5},
  50. tuple_type{"second", 6},
  51. tuple_type{"third", 5},
  52. tuple_type{"etc...", 6}
  53. }));
  54. REQUIRE(strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)));
  55. }
  56. #ifdef __cpp_structured_bindings
  57. // Structured bindings make the table utility much nicer to use
  58. TEST_CASE( "strlen2", "[approvals][generators]" ) {
  59. using tuple_type = std::tuple<std::string, int>; // see above workaround
  60. auto [test_input, expected] =
  61. GENERATE( table<std::string, size_t>( { tuple_type{ "one", 3 },
  62. tuple_type{ "two", 3 },
  63. tuple_type{ "three", 5 },
  64. tuple_type{ "four", 4 } } ) );
  65. REQUIRE( test_input.size() == expected );
  66. }
  67. #endif
  68. // An alternate way of doing data tables without structured bindings
  69. struct Data { std::string str; size_t len; };
  70. TEST_CASE( "strlen3", "[generators]" ) {
  71. auto data = GENERATE( values<Data>({
  72. {"one", 3},
  73. {"two", 3},
  74. {"three", 5},
  75. {"four", 4}
  76. }));
  77. REQUIRE( data.str.size() == data.len );
  78. }
  79. #ifdef __cpp_structured_bindings
  80. // Based on example from https://docs.cucumber.io/gherkin/reference/#scenario-outline
  81. // (thanks to https://github.com/catchorg/Catch2/issues/850#issuecomment-399504851)
  82. // Note that GIVEN, WHEN, and THEN now forward onto DYNAMIC_SECTION instead of SECTION.
  83. // DYNAMIC_SECTION takes its name as a stringstream-style expression, so can be formatted using
  84. // variables in scope - such as the generated variables here. This reads quite nicely in the
  85. // test name output (the full scenario description).
  86. static auto eatCucumbers( int start, int eat ) -> int { return start-eat; }
  87. SCENARIO("Eating cucumbers", "[generators][approvals]") {
  88. using tuple_type = std::tuple<int, int, int>;
  89. auto [start, eat, left] = GENERATE( table<int, int, int>(
  90. { tuple_type{ 12, 5, 7 }, tuple_type{ 20, 5, 15 } } ) );
  91. GIVEN( "there are " << start << " cucumbers" )
  92. WHEN( "I eat " << eat << " cucumbers" )
  93. THEN( "I should have " << left << " cucumbers" ) {
  94. REQUIRE( eatCucumbers( start, eat ) == left );
  95. }
  96. }
  97. #endif
  98. // There are also some generic generator manipulators
  99. TEST_CASE("Generators -- adapters", "[generators][generic]") {
  100. // TODO: This won't work yet, introduce GENERATE_VAR?
  101. //auto numbers = Catch::Generators::values({ 1, 2, 3, 4, 5, 6 });
  102. SECTION("Filtering by predicate") {
  103. SECTION("Basic usage") {
  104. // This filters out all odd (false) numbers, giving [2, 4, 6]
  105. auto i = GENERATE(filter([] (int val) { return val % 2 == 0; }, values({ 1, 2, 3, 4, 5, 6 })));
  106. REQUIRE(i % 2 == 0);
  107. }
  108. SECTION("Throws if there are no matching values") {
  109. using namespace Catch::Generators;
  110. REQUIRE_THROWS_AS(filter([] (int) {return false; }, value(1)), Catch::GeneratorException);
  111. }
  112. }
  113. SECTION("Shortening a range") {
  114. // This takes the first 3 elements from the values, giving back [1, 2, 3]
  115. auto i = GENERATE(take(3, values({ 1, 2, 3, 4, 5, 6 })));
  116. REQUIRE(i < 4);
  117. }
  118. SECTION("Transforming elements") {
  119. SECTION("Same type") {
  120. // This doubles values [1, 2, 3] into [2, 4, 6]
  121. auto i = GENERATE(map([] (int val) { return val * 2; }, values({ 1, 2, 3 })));
  122. REQUIRE(i % 2 == 0);
  123. }
  124. SECTION("Different type") {
  125. // This takes a generator that returns ints and maps them into strings
  126. auto i = GENERATE(map<std::string>([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 })));
  127. REQUIRE(i.size() == 1);
  128. }
  129. SECTION("Different deduced type") {
  130. // This takes a generator that returns ints and maps them into strings
  131. auto i = GENERATE(map([] (int val) { return std::to_string(val); }, values({ 1, 2, 3 })));
  132. REQUIRE(i.size() == 1);
  133. }
  134. }
  135. SECTION("Repeating a generator") {
  136. // This will return values [1, 2, 3, 1, 2, 3]
  137. auto j = GENERATE(repeat(2, values({ 1, 2, 3 })));
  138. REQUIRE(j > 0);
  139. }
  140. SECTION("Chunking a generator into sized pieces") {
  141. SECTION("Number of elements in source is divisible by chunk size") {
  142. auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3, 3 })));
  143. REQUIRE(chunk2.size() == 2);
  144. REQUIRE(chunk2.front() == chunk2.back());
  145. }
  146. SECTION("Number of elements in source is not divisible by chunk size") {
  147. auto chunk2 = GENERATE(chunk(2, values({ 1, 1, 2, 2, 3 })));
  148. REQUIRE(chunk2.size() == 2);
  149. REQUIRE(chunk2.front() == chunk2.back());
  150. REQUIRE(chunk2.front() < 3);
  151. }
  152. SECTION("Chunk size of zero") {
  153. auto chunk2 = GENERATE(take(3, chunk(0, value(1))));
  154. REQUIRE(chunk2.size() == 0);
  155. }
  156. SECTION("Throws on too small generators") {
  157. using namespace Catch::Generators;
  158. REQUIRE_THROWS_AS(chunk(2, value(1)), Catch::GeneratorException);
  159. }
  160. }
  161. }
  162. // Note that because of the non-reproducibility of distributions,
  163. // anything involving the random generators cannot be part of approvals
  164. TEST_CASE("Random generator", "[generators][approvals]") {
  165. SECTION("Infer int from integral arguments") {
  166. auto val = GENERATE(take(4, random(0, 1)));
  167. STATIC_REQUIRE(std::is_same<decltype(val), int>::value);
  168. REQUIRE(0 <= val);
  169. REQUIRE(val <= 1);
  170. }
  171. SECTION("Infer double from double arguments") {
  172. auto val = GENERATE(take(4, random(0., 1.)));
  173. STATIC_REQUIRE(std::is_same<decltype(val), double>::value);
  174. REQUIRE(0. <= val);
  175. REQUIRE(val < 1);
  176. }
  177. }
  178. TEST_CASE("Nested generators and captured variables", "[generators]") {
  179. // Workaround for old libstdc++
  180. using record = std::tuple<int, int>;
  181. // Set up 3 ranges to generate numbers from
  182. auto extent = GENERATE(table<int, int>({
  183. record{3, 7},
  184. record{-5, -3},
  185. record{90, 100}
  186. }));
  187. auto from = std::get<0>(extent);
  188. auto to = std::get<1>(extent);
  189. auto values = GENERATE_COPY(range(from, to));
  190. REQUIRE(values > -6);
  191. }
  192. namespace {
  193. size_t call_count = 0;
  194. size_t test_count = 0;
  195. std::vector<int> make_data() {
  196. return { 1, 3, 5, 7, 9, 11 };
  197. }
  198. std::vector<int> make_data_counted() {
  199. ++call_count;
  200. return make_data();
  201. }
  202. }
  203. #if defined(__clang__)
  204. #pragma clang diagnostic push
  205. #pragma clang diagnostic ignored "-Wexit-time-destructors"
  206. #endif
  207. TEST_CASE("Copy and then generate a range", "[generators]") {
  208. SECTION("from var and iterators") {
  209. static auto data = make_data();
  210. // It is important to notice that a generator is only initialized
  211. // **once** per run. What this means is that modifying data will not
  212. // modify the underlying generator.
  213. auto elem = GENERATE_REF(from_range(data.begin(), data.end()));
  214. REQUIRE(elem % 2 == 1);
  215. }
  216. SECTION("From a temporary container") {
  217. auto elem = GENERATE(from_range(make_data_counted()));
  218. ++test_count;
  219. REQUIRE(elem % 2 == 1);
  220. }
  221. SECTION("Final validation") {
  222. REQUIRE(call_count == 1);
  223. REQUIRE(make_data().size() == test_count);
  224. }
  225. }
  226. TEST_CASE("#1913 - GENERATE inside a for loop should not keep recreating the generator", "[regression][generators]") {
  227. static int counter = 0;
  228. for (int i = 0; i < 3; ++i) {
  229. int _ = GENERATE(1, 2);
  230. (void)_;
  231. ++counter;
  232. }
  233. // There should be at most 6 (3 * 2) counter increments
  234. REQUIRE(counter < 7);
  235. }
  236. TEST_CASE("#1913 - GENERATEs can share a line", "[regression][generators]") {
  237. int i = GENERATE(1, 2); int j = GENERATE(3, 4);
  238. REQUIRE(i != j);
  239. }
  240. #if defined(__clang__)
  241. #pragma clang diagnostic pop
  242. #endif