1 #include <catch2/catch.hpp>
2 #include <rapidcheck/catch.h>
3 
4 #include "rapidcheck/gen/Transform.h"
5 #include "rapidcheck/gen/Create.h"
6 
7 #include "util/GenUtils.h"
8 #include "util/Predictable.h"
9 #include "util/Generators.h"
10 #include "util/ShrinkableUtils.h"
11 
12 using namespace rc;
13 using namespace rc::test;
14 
15 TEST_CASE("gen::map") {
16   prop("maps the shrinkable returned by the generator",
__anon5eb492820102(const Shrinkable<int> &shrinkable) 17        [](const Shrinkable<int> &shrinkable) {
18          const auto mapper = [](int x) { return x * x; };
19          const auto mapped =
20              gen::map(Gen<int>(fn::constant(shrinkable)), mapper)(Random(), 0);
21          RC_ASSERT(shrinkable::map(shrinkable, mapper) == mapped);
22        });
23 
24   prop("forwards the parameter to the generator",
__anon5eb492820302(const GenParams &params) 25        [](const GenParams &params) {
26          const auto gen = gen::map(genPassedParams(),
27                                    [](GenParams &&x) { return std::move(x); });
28          RC_ASSERT(gen(params.random, params.size).value() == params);
29        });
30 
31   SECTION("works with non-copyable types") {
32     const auto value =
33         gen::map(gen::arbitrary<NonCopyable>(),
__anon5eb492820502(NonCopyable &&nc) 34                  [](NonCopyable &&nc) { return std::move(nc); })(Random(), 0)
35             .value();
36     REQUIRE(isArbitraryPredictable(value));
37   }
38 
39   prop("uses gen::arbitrary if no generator is specified",
__anon5eb492820602(const GenParams &params) 40        [](const GenParams &params) {
41          const auto value = gen::map<Predictable>([](Predictable &&x) {
42            return std::move(x);
43          })(params.random, params.size)
44                                 .value();
45          RC_ASSERT(isArbitraryPredictable(value));
46        });
47 
48   prop(
49       "finds minimum where string represtation of unsigned integer must be"
50       " longer than some value",
__anon5eb492820802(const Random &random) 51       [](const Random &random) {
52         const auto gen =
53             gen::map(gen::arbitrary<unsigned int>(),
54                      [](unsigned int x) { return std::to_string(x); });
55         const auto n = *gen::inRange<std::size_t>(2, 7);
56         std::string expected(n, '0');
57         expected[0] = '1';
58         const auto result =
59             searchGen(random,
60                       kNominalSize,
61                       gen,
62                       [=](const std::string &x) { return x.size() >= n; });
63         RC_ASSERT(result == expected);
64       });
65 }
66 
67 TEST_CASE("gen::mapcat") {
68   // It would be nice and all to test the monad laws here but since `Gen` is
69   // only morally a monad because of random splitting, it's simpler this way
70   prop("mapcats the returned shrinkables",
__anon5eb492820b02(Shrinkable<int> a, Shrinkable<int> b) 71        [](Shrinkable<int> a, Shrinkable<int> b) {
72          const auto expected = shrinkable::mapcat(
73              a,
74              [=](int x) {
75                return shrinkable::map(
76                    b, [=](int y) { return std::make_pair(x, y); });
77              });
78          const auto gen = gen::mapcat<int>(
79              fn::constant(a),
80              [=](int x) -> Gen<std::pair<int, int>> {
81                return fn::constant(shrinkable::map(
82                    b, [=](int y) { return std::make_pair(x, y); }));
83              });
84          const auto actual = gen(Random(), 0);
85 
86          RC_ASSERT(actual == expected);
87        });
88 
89   prop("passes correct size",
__anon5eb492821002(const GenParams &params) 90        [](const GenParams &params) {
91          const auto gen = gen::mapcat(
92              genSize(),
93              [](int x) {
94                return gen::map(genSize(),
95                                [=](int y) { return std::make_pair(x, y); });
96              });
97 
98          const auto value = gen(params.random, params.size).value();
99          RC_ASSERT(value == std::make_pair(params.size, params.size));
100        });
101 
102   prop("passes unique random generators",
__anon5eb492821302(const GenParams &params) 103        [](const GenParams &params) {
104          const auto gen = gen::mapcat(
105              genRandom(),
106              [](const Random &x) {
107                return gen::map(
108                    genRandom(),
109                    [=](Random &&y) { return std::make_pair(x, std::move(y)); });
110              });
111 
112          const auto value = gen(params.random, params.size).value();
113          RC_ASSERT(value.first != value.second);
114          RC_ASSERT(value.first != params.random);
115          RC_ASSERT(value.second != params.random);
116        });
117 
118   SECTION("works with non-copyable types") {
119     const auto gen = gen::mapcat(gen::arbitrary<NonCopyable>(),
__anon5eb492821602(NonCopyable &&x) 120                                  [](NonCopyable &&x) {
121                                    RC_ASSERT(isArbitraryPredictable(x));
122                                    return gen::arbitrary<NonCopyable>();
123                                  });
124     const auto value = gen(Random(), 0).value();
125     RC_ASSERT(isArbitraryPredictable(value));
126   }
127 }
128 
129 TEST_CASE("gen::join") {
130   prop("gen::join(gen::map(s, f)) == gen::mapcat(s, f)",
__anon5eb492821702(Shrinkable<int> a, Shrinkable<int> b) 131        [](Shrinkable<int> a, Shrinkable<int> b) {
132          const auto f = [=](int x) -> Gen<std::pair<int, int>> {
133            return fn::constant(
134                shrinkable::map(b, [=](int y) { return std::make_pair(x, y); }));
135          };
136          const auto expected =
137              gen::mapcat(Gen<int>(fn::constant(a)), f)(Random(), 0);
138          const auto actual =
139              gen::join(gen::map(Gen<int>(fn::constant(a)), f))(Random(), 0);
140 
141          RC_ASSERT(actual == expected);
142        });
143 
144   prop("passes correct size",
__anon5eb492821a02(const GenParams &params) 145        [](const GenParams &params) {
146          const auto gen = gen::join(gen::map(
147              genSize(),
148              [=](int x) {
149                return gen::map(genSize(),
150                                [=](int y) { return std::make_pair(x, y); });
151              }));
152 
153          const auto value = gen(params.random, params.size).value();
154          RC_ASSERT(value == std::make_pair(params.size, params.size));
155        });
156 
157   prop("passes unique random generators",
__anon5eb492821d02(const GenParams &params) 158        [](const GenParams &params) {
159          const auto gen =
160              gen::join(gen::map(genRandom(),
161                                 [=](const Random &x) {
162                                   return gen::map(genRandom(),
163                                                   [=](const Random &y) {
164                                                     return std::make_pair(x, y);
165                                                   });
166                                 }));
167 
168          const auto value = gen(params.random, params.size).value();
169          RC_ASSERT(value.first != value.second);
170          RC_ASSERT(value.first != params.random);
171          RC_ASSERT(value.second != params.random);
172        });
173 
174   SECTION("works with non-copyable types") {
175     const auto gen = gen::join(gen::map(gen::arbitrary<NonCopyable>(),
__anon5eb492822002(const NonCopyable &x) 176                                         [](const NonCopyable &x) {
177                                           RC_ASSERT(isArbitraryPredictable(x));
178                                           return gen::arbitrary<NonCopyable>();
179                                         }));
180 
181     const auto value = gen(Random(), 0).value();
182     RC_ASSERT(isArbitraryPredictable(value));
183   }
184 }
185 
186 TEST_CASE("gen::apply") {
187   prop("has tuple shrinking semantics",
__anon5eb492822102null188        [] {
189          const auto g1 = genFixedCountdown(*gen::inRange(0, 10));
190          const auto g2 = genFixedCountdown(*gen::inRange(0, 10));
191          const auto g3 = genFixedCountdown(*gen::inRange(0, 10));
192 
193          const auto gen = gen::apply([](int a, int b, int c) {
194            return std::make_tuple(a, b, c);
195          }, g1, g2, g3);
196          const auto tupleGen = gen::tuple(g1, g2, g3);
197 
198          assertEquivalent(gen(Random(), 0), tupleGen(Random(), 0));
199        });
200 
201   prop("passes correct size",
__anon5eb492822302(const GenParams &params) 202        [](const GenParams &params) {
203          const auto gen = gen::apply([](int a, int b, int c) {
204            return std::make_tuple(a, b, c);
205          }, genSize(), genSize(), genSize());
206          const auto value = gen(params.random, params.size).value();
207 
208          RC_ASSERT(value ==
209                    std::make_tuple(params.size, params.size, params.size));
210        });
211 
212   prop("passed random generators are unique",
__anon5eb492822502(const GenParams &params) 213        [](const GenParams &params) {
214          const auto gen = gen::apply([](Random &&a, Random &&b, Random &&c) {
215            return std::make_tuple(std::move(a), std::move(b), std::move(c));
216          }, genRandom(), genRandom(), genRandom());
217          const auto value = gen(params.random, params.size).value();
218 
219          RC_ASSERT(std::get<0>(value) != std::get<1>(value));
220          RC_ASSERT(std::get<0>(value) != std::get<2>(value));
221          RC_ASSERT(std::get<1>(value) != std::get<2>(value));
222        });
223 
224   SECTION("works with non-copyable types") {
__anon5eb492822702(NonCopyable &&a, NonCopyable &&b) 225     const auto gen = gen::apply([](NonCopyable &&a, NonCopyable &&b) {
226       return std::make_tuple(std::move(a), std::move(b));
227     }, gen::arbitrary<NonCopyable>(), gen::arbitrary<NonCopyable>());
228     const auto value = gen(Random(), 0).value();
229 
230     RC_ASSERT(isArbitraryPredictable(std::get<0>(value)));
231     RC_ASSERT(isArbitraryPredictable(std::get<1>(value)));
232   }
233 }
234 
235 TEST_CASE("gen::cast") {
236   prop("casting to a larger type and then back yields original",
__anon5eb492822802(const Shrinkable<uint8_t> &shrinkable) 237        [](const Shrinkable<uint8_t> &shrinkable) {
238          const Gen<uint8_t> gen(fn::constant(shrinkable));
239          const auto cast = gen::cast<uint8_t>(gen::cast<int>(gen));
240          RC_ASSERT(cast(Random(), 0) == shrinkable);
241        });
242 }
243 
244 TEST_CASE("gen::resize") {
245   prop("always uses the specified size",
__anon5eb492822902(const GenParams &params) 246        [](const GenParams &params) {
247          const auto size = *gen::inRange<int>(0, 2000);
248          const auto gen = gen::resize(size, genPassedParams());
249          const auto value = gen(params.random, params.size).value();
250          RC_ASSERT(value.size == size);
251        });
252 
253   prop("passes through random generator unchanged",
__anon5eb492822a02(const GenParams &params) 254        [](const GenParams &params) {
255          const auto gen = gen::resize(0, genPassedParams());
256          const auto value = gen(params.random, params.size).value();
257          RC_ASSERT(value.random == params.random);
258        });
259 }
260 
261 TEST_CASE("gen::scale") {
262   prop("scales the size by the specified factor",
__anon5eb492822b02(const GenParams &params) 263        [](const GenParams &params) {
264          const auto gen = gen::scale(2.0, genPassedParams());
265          const auto value = gen(params.random, params.size).value();
266          RC_ASSERT(value.size == params.size * 2);
267        });
268 
269   prop("passes through random generator unchanged",
__anon5eb492822c02(const GenParams &params) 270        [](const GenParams &params) {
271          const auto gen = gen::scale(2.0, genPassedParams());
272          const auto value = gen(params.random, params.size).value();
273          RC_ASSERT(value.random == params.random);
274        });
275 }
276 
277 TEST_CASE("gen::noShrink") {
278   prop("returned shrinkable has expected value",
__anon5eb492822d02(const GenParams &params, int x) 279        [](const GenParams &params, int x) {
280          const auto gen = gen::noShrink(gen::just(x));
281          const auto value = gen(params.random, params.size).value();
282          RC_ASSERT(value == x);
283        });
284 
285   prop("returned shrinkable has no shrinks",
__anon5eb492822e02(const GenParams &params) 286        [](const GenParams &params) {
287          const auto gen = gen::noShrink(gen::arbitrary<int>());
288          const auto shrinkable = gen(params.random, params.size);
289          RC_ASSERT(!shrinkable.shrinks().next());
290        });
291 
292   prop("passes generation params unchanged",
__anon5eb492822f02(const GenParams &params) 293        [](const GenParams &params) {
294          const auto gen = gen::noShrink(genPassedParams());
295          const auto value = gen(params.random, params.size).value();
296          RC_ASSERT(value == params);
297        });
298 }
299 
300 TEST_CASE("gen::withSize") {
301   prop("passes the current size to the callable",
__anon5eb492823002(const GenParams &params) 302        [](const GenParams &params) {
303          const auto gen =
304              gen::withSize([](int size) { return gen::just(size); });
305          const auto value = gen(params.random, params.size).value();
306          RC_ASSERT(value == params.size);
307        });
308 
309   prop("generates what the returned generator generates",
__anon5eb492823202(const GenParams &params, int x) 310        [](const GenParams &params, int x) {
311          const auto gen = gen::withSize([=](int size) { return gen::just(x); });
312          const auto shrinkable = gen(params.random, params.size);
313          RC_ASSERT(shrinkable == shrinkable::just(x));
314        });
315 
316   prop("passes generation params unchanged",
__anon5eb492823402(const GenParams &params) 317        [](const GenParams &params) {
318          const auto gen =
319              gen::withSize([](int size) { return genPassedParams(); });
320          const auto value = gen(params.random, params.size).value();
321          RC_ASSERT(value == params);
322        });
323 }
324 
325 TEST_CASE("gen::shrink") {
326   prop("passes generation params unchanged",
__anon5eb492823602(const GenParams &params) 327        [](const GenParams &params) {
328          const auto gen =
329              gen::shrink(genPassedParams(), fn::constant(Seq<GenParams>()));
330          const auto value = gen(params.random, params.size).value();
331          RC_ASSERT(value == params);
332        });
333 
334   prop("applies postShrink to returned Shrinkable",
__anon5eb492823702(const GenParams &params) 335        [](const GenParams &params) {
336          const auto gen = gen::arbitrary<int>();
337          const auto f = [](int v) {
338            return seq::takeWhile(seq::iterate(v, [](int x) { return x / 2; }),
339                                  [](int x) { return x > 0; });
340          };
341          const auto expected =
342              shrinkable::postShrink(gen(params.random, params.size), f);
343          const auto actual = gen::shrink(gen, f)(params.random, params.size);
344 
345          assertEquivalent(actual, expected);
346        });
347 }
348