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