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 ¶ms) 25 [](const GenParams ¶ms) { 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 ¶ms) 40 [](const GenParams ¶ms) { 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 ¶ms) 90 [](const GenParams ¶ms) { 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 ¶ms) 103 [](const GenParams ¶ms) { 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 ¶ms) 145 [](const GenParams ¶ms) { 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 ¶ms) 158 [](const GenParams ¶ms) { 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 ¶ms) 202 [](const GenParams ¶ms) { 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 ¶ms) 213 [](const GenParams ¶ms) { 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 ¶ms) 246 [](const GenParams ¶ms) { 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 ¶ms) 254 [](const GenParams ¶ms) { 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 ¶ms) 263 [](const GenParams ¶ms) { 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 ¶ms) 270 [](const GenParams ¶ms) { 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 ¶ms, int x) 279 [](const GenParams ¶ms, 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 ¶ms) 286 [](const GenParams ¶ms) { 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 ¶ms) 293 [](const GenParams ¶ms) { 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 ¶ms) 302 [](const GenParams ¶ms) { 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 ¶ms, int x) 310 [](const GenParams ¶ms, 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 ¶ms) 317 [](const GenParams ¶ms) { 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 ¶ms) 327 [](const GenParams ¶ms) { 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 ¶ms) 335 [](const GenParams ¶ms) { 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