1 #include <catch2/catch.hpp> 2 #include <rapidcheck/catch.h> 3 4 #include <cctype> 5 6 #include "rapidcheck/shrink/Shrink.h" 7 #include "rapidcheck/seq/Operations.h" 8 9 #include "util/Util.h" 10 #include "util/Meta.h" 11 #include "util/TypeListMacros.h" 12 13 using namespace rc; 14 using namespace rc::test; 15 16 namespace { 17 18 struct RemoveChunksProperties { 19 template <typename T> exec__anon6cdd066a0111::RemoveChunksProperties20 static void exec() { 21 static const auto fewValues = gen::scale(0.3, gen::arbitrary<T>()); 22 // TODO non-empty generator 23 static const auto fewNonEmptyValues = 24 gen::suchThat(fewValues, [](const T &x) { return !x.empty(); }); 25 26 templatedProp<T>("first tries empty collection", 27 [] { 28 const auto collection = *fewNonEmptyValues; 29 RC_ASSERT( 30 shrink::removeChunks(collection).next()->empty()); 31 }); 32 33 templatedProp<T>("successively increases in size for each shrink", 34 [] { 35 const auto seq = shrink::removeChunks(*fewValues); 36 T c; 37 seq::forEach(std::move(seq), 38 [&](T &&next) { 39 RC_ASSERT(next.size() >= c.size()); 40 c = std::move(next); 41 }); 42 }); 43 44 templatedProp<T>("shrinks to a subset of the original", 45 [] { 46 const auto elements = *fewValues; 47 const auto seq = shrink::removeChunks(elements); 48 seq::forEach(std::move(seq), 49 [&](T &&c) { 50 auto diff( 51 setDifference<char>(c, elements)); 52 RC_ASSERT(diff.size() == 0U); 53 }); 54 }); 55 56 templatedProp<T>( 57 "every removal of consecutive elements is a possible shrink", 58 [] { 59 const auto elements = *fewNonEmptyValues; 60 const auto size = int(elements.size()); 61 const auto a = *gen::inRange(0, size + 1); 62 const auto b = *gen::distinctFrom(gen::inRange(0, size + 1), a); 63 const auto left = std::min(a, b); 64 const auto right = std::max(a, b); 65 66 T shrink; 67 shrink.reserve(size - (right - left)); 68 shrink.insert(end(shrink), begin(elements), begin(elements) + left); 69 shrink.insert(end(shrink), begin(elements) + right, end(elements)); 70 71 RC_ASSERT(seq::contains(shrink::removeChunks(elements), shrink)); 72 }); 73 74 templatedProp<T>( 75 "never yields the original value", 76 [] { 77 auto elements = *fewValues; 78 RC_ASSERT(!seq::contains(shrink::removeChunks(elements), elements)); 79 }); 80 } 81 }; 82 83 } // namespace 84 85 TEST_CASE("shrink::removeChunks") { 86 forEachType<RemoveChunksProperties, std::vector<char>, std::string>(); 87 } 88 89 namespace { 90 91 struct EachElementProperties { 92 template <typename T> exec__anon6cdd066a0a11::EachElementProperties93 static void exec() { 94 templatedProp<T>( 95 "every shrink for every element is tried in order", 96 [] { 97 const auto elements = *gen::container<T>(gen::nonNegative<char>()); 98 auto seq = shrink::eachElement( 99 elements, [=](char x) { return seq::range(x - 1, -1); }); 100 101 for (std::size_t i = 0; i < elements.size(); i++) { 102 auto x = elements[i]; 103 while (x > 0) { 104 auto expected = elements; 105 expected[i] = --x; 106 RC_ASSERT(*seq.next() == expected); 107 } 108 } 109 110 RC_ASSERT(!seq.next()); 111 }); 112 } 113 }; 114 115 } // namespace 116 117 TEST_CASE("shrink::eachElement") { 118 forEachType<EachElementProperties, std::vector<char>, std::string>(); 119 } 120 121 namespace { 122 123 struct ShrinkTowardsProperties { 124 template <typename T> exec__anon6cdd066a0d11::ShrinkTowardsProperties125 static void exec() { 126 templatedProp<T>("first tries target immediately", 127 [](T target) { 128 T value = *gen::distinctFrom(target); 129 auto seq = shrink::towards(value, target); 130 auto first = seq.next(); 131 RC_ASSERT(first); 132 RC_ASSERT(*first == target); 133 }); 134 135 templatedProp<T>("tries an adjacent value last", 136 [](T target) { 137 T value = *gen::distinctFrom(target); 138 auto seq = shrink::towards(value, target); 139 auto fin = seq::last(seq); 140 RC_ASSERT(fin); 141 T diff = 142 (value > target) ? (value - *fin) : (*fin - value); 143 RC_ASSERT(diff == T(1)); 144 }); 145 146 templatedProp<T>( 147 "shrinking towards self yields empty shrink", 148 [](T target) { RC_ASSERT(!shrink::towards(target, target).next()); }); 149 150 templatedProp<T>( 151 "never contains original value", 152 [](T x, T y) { RC_ASSERT(!seq::contains(shrink::towards(x, y), x)); }); 153 } 154 }; 155 156 } // namespace 157 158 TEST_CASE("shrink::towards") { 159 forEachType<ShrinkTowardsProperties, RC_INTEGRAL_TYPES>(); 160 } 161 162 namespace { 163 164 struct IntegralProperties { 165 template <typename T> exec__anon6cdd066a1211::IntegralProperties166 static void exec() { 167 templatedProp<T>("always tries zero first", 168 [] { 169 T value = *gen::nonZero<T>(); 170 RC_ASSERT(*shrink::integral<T>(value).next() == T(0)); 171 }); 172 173 TEMPLATED_SECTION(T, "zero has no shrinks") { 174 REQUIRE(!shrink::integral<T>(0).next()); 175 } 176 177 templatedProp<T>( 178 "never contains original value", 179 [](T x) { RC_ASSERT(!seq::contains(shrink::integral<T>(x), x)); }); 180 } 181 }; 182 183 struct SignedIntegralProperties { 184 template <typename T> exec__anon6cdd066a1211::SignedIntegralProperties185 static void exec() { 186 templatedProp<T>( 187 "shrinks negative values to their positive equivalent", 188 [] { 189 T value = *gen::suchThat( 190 gen::negative<T>(), 191 [](T x) { return x > std::numeric_limits<T>::min(); }); 192 RC_ASSERT(seq::contains<T>(shrink::integral<T>(value), -value)); 193 }); 194 195 templatedProp<T>("always tries zero first", 196 [] { 197 T value = *gen::nonZero<T>(); 198 RC_ASSERT(*shrink::integral<T>(value).next() == T(0)); 199 }); 200 201 TEMPLATED_SECTION(T, "zero has no shrinks") { 202 REQUIRE(!shrink::integral<T>(0).next()); 203 } 204 } 205 }; 206 207 } // namespace 208 209 TEST_CASE("shrink::integral") { 210 forEachType<IntegralProperties, RC_INTEGRAL_TYPES>(); 211 forEachType<SignedIntegralProperties, RC_SIGNED_INTEGRAL_TYPES>(); 212 } 213 214 namespace { 215 216 struct RealProperties { 217 template <typename T> exec__anon6cdd066a1811::RealProperties218 static void exec() { 219 templatedProp<T>( 220 "shrinks to nearest integer", 221 [] { 222 // TODO I'm not super fond of this way of generating 223 const auto integer = *gen::arbitrary<int16_t>(); 224 const auto value = 225 static_cast<T>(integer + *gen::element(0.1, 0.2, 0.5, 0.7, 0.9)); 226 RC_PRE(value != std::trunc(value)); 227 RC_ASSERT(seq::contains(shrink::real(value), std::trunc(value))); 228 }); 229 230 TEMPLATED_SECTION(T, "zero has no shrinks") { 231 REQUIRE(!shrink::real<T>(T(0.0)).next()); 232 } 233 234 templatedProp<T>("tries 0.0 first", 235 [] { 236 T value = *gen::nonZero<T>(); 237 RC_ASSERT(*shrink::real<T>(value).next() == T(0.0)); 238 }); 239 240 templatedProp<T>( 241 "never contains original value", 242 [](T x) { RC_ASSERT(!seq::contains(shrink::real<T>(x), x)); }); 243 } 244 }; 245 246 } // namespace 247 248 TEST_CASE("shrink::real") { forEachType<RealProperties, RC_REAL_TYPES>(); } 249 250 TEST_CASE("shrink::bool") { 251 SECTION("shrinks 'true' to 'false'") { 252 REQUIRE(shrink::boolean(true) == seq::just(false)); 253 } 254 255 SECTION("does not shrink 'false'") { 256 REQUIRE(!shrink::boolean(false).next()); 257 } 258 } 259 260 struct CharacterProperties { 261 template <typename T> execCharacterProperties262 static void exec() { 263 templatedProp<T>("'a' is the first shrink", 264 [](T x) { 265 RC_PRE(x != 'a'); 266 RC_ASSERT(*shrink::character(x).next() == 'a'); 267 }); 268 269 templatedProp<T>( 270 "if uppercase, contains lowercase", 271 [] { 272 static const std::string letters("ABCDEFGHIJKLMNOPQRSTUVXYZ"); 273 T x = *gen::elementOf(letters); 274 RC_ASSERT(seq::contains<T>(shrink::character(x), std::tolower(x))); 275 }); 276 277 templatedProp<T>( 278 "never contains self", 279 [](T x) { RC_ASSERT(!seq::contains(shrink::character(x), x)); }); 280 } 281 }; 282 283 TEST_CASE("shrink::character") { 284 forEachType<CharacterProperties, char, wchar_t>(); 285 } 286