1 #pragma once
2
3 #include "util/GenUtils.h"
4 #include "util/ShrinkableUtils.h"
5 #include "util/Util.h"
6 #include "util/Meta.h"
7 #include "util/TypeListMacros.h"
8
9 namespace rc {
10 namespace test {
11
12 struct ContainerFactory {
13 template <typename Container, typename T>
makeGenContainerFactory14 static Gen<Container> makeGen(Gen<T> gen) {
15 return gen::container<Container>(std::move(gen));
16 }
17
18 template <typename Container, typename T>
makeGenContainerFactory19 static Gen<Container> makeGen(std::size_t count, Gen<T> gen) {
20 return gen::container<Container>(count, std::move(gen));
21 }
22 };
23
24 template <std::size_t Min, std::size_t Max>
25 struct FixedContainerFactory {
26 template <typename Container, typename T>
makeGenFixedContainerFactory27 static Gen<Container> makeGen(Gen<T> gen) {
28 return gen::container<Container>(*gen::inRange(Min, Max), std::move(gen));
29 }
30 };
31
32 struct MapFactory {
33 template <typename Map, typename T>
makeGenMapFactory34 static Gen<Map> makeGen(Gen<T> gen) {
35 return gen::container<Map>(gen, gen);
36 }
37
38 template <typename Map, typename T>
makeGenMapFactory39 static Gen<Map> makeGen(std::size_t count, Gen<T> gen) {
40 return gen::container<Map>(count, gen, gen);
41 }
42 };
43
44 template <std::size_t Min, std::size_t Max>
45 struct FixedMapFactory {
46 template <typename Container, typename T>
makeGenFixedMapFactory47 static Gen<Container> makeGen(Gen<T> gen) {
48 return gen::container<Container>(*gen::inRange(Min, Max), gen, gen);
49 }
50 };
51
hasSize(int size,const std::pair<const GenParams,GenParams> & p)52 inline bool hasSize(int size, const std::pair<const GenParams, GenParams> &p) {
53 return (p.first.size == size) && (p.second.size == size);
54 }
55
hasSize(int size,const GenParams & params)56 inline bool hasSize(int size, const GenParams ¶ms) { return size == params.size; }
57
insertRandoms(std::unordered_set<Random> & randoms,const std::pair<const GenParams,GenParams> & p)58 inline bool insertRandoms(std::unordered_set<Random> &randoms,
59 const std::pair<const GenParams, GenParams> &p) {
60 return randoms.insert(p.first.random).second &&
61 randoms.insert(p.second.random).second;
62 }
63
insertRandoms(std::unordered_set<Random> & randoms,const GenParams & params)64 inline bool insertRandoms(std::unordered_set<Random> &randoms,
65 const GenParams ¶ms) {
66 return randoms.insert(params.random).second;
67 }
68
69 template <typename Factory>
70 struct GenericProperties {
71 template <typename T>
execGenericProperties72 static void exec() {
73 templatedProp<T>(
74 "generated container never has more elements than the current size",
75 [](const GenParams ¶ms) {
76 const auto value = Factory::template makeGen<T>(genCountdown())(
77 params.random, params.size)
78 .value();
79 RC_ASSERT(std::distance(begin(value), end(value)) <= params.size);
80 });
81
82 templatedProp<T>("first shrink is empty",
83 [](const GenParams ¶ms) {
84 const auto shrinkable = Factory::template makeGen<T>(
85 genCountdown())(params.random, params.size);
86 RC_PRE(!shrinkable.value().empty());
87 RC_ASSERT(shrinkable.shrinks().next()->value().empty());
88 });
89
90 templatedProp<T>(
91 "the size of each shrink is the same or smaller than the original",
92 [](const GenParams ¶ms) {
93 const auto shrinkable = Factory::template makeGen<T>(genCountdown())(
94 params.random, params.size);
95 onAnyPath(
96 shrinkable,
97 [](const Shrinkable<T> &value, const Shrinkable<T> &shrink) {
98 RC_ASSERT(containerSize(shrink.value()) <=
99 containerSize(value.value()));
100 });
101 });
102
103 templatedProp<T>("none of the shrinks equal the original value",
104 [](const GenParams ¶ms) {
105 const auto shrinkable = Factory::template makeGen<T>(
106 genCountdown())(params.random, params.size);
107 onAnyPath(shrinkable,
108 [](const Shrinkable<T> &value,
109 const Shrinkable<T> &shrink) {
110 RC_ASSERT(value.value() != shrink.value());
111 });
112 });
113 }
114 };
115
116 template <typename Factory>
117 struct ParamsProperties {
118 template <typename T>
execParamsProperties119 static void exec() {
120 using Element = typename T::value_type;
121
122 templatedProp<T>("passes the correct size to the element generators",
123 [](const GenParams ¶ms) {
124 const auto value =
125 Factory::template makeGen<T>(genPassedParams())(
126 params.random, params.size)
127 .value();
128 RC_ASSERT(std::all_of(begin(value),
129 end(value),
130 [&](const Element &x) {
131 return hasSize(params.size, x);
132 }));
133 });
134
135 templatedProp<T>(
136 "the random generators passed to element generators are unique",
137 [](const GenParams ¶ms) {
138 const auto value = Factory::template makeGen<T>(genPassedParams())(
139 params.random, params.size)
140 .value();
141 std::unordered_set<Random> randoms;
142 RC_ASSERT(std::all_of(
143 begin(value),
144 end(value),
145 [&](const Element &x) { return insertRandoms(randoms, x); }));
146 });
147 }
148 };
149
150 template <typename Factory>
151 struct RetrialProperties {
152 template <typename T>
execRetrialProperties153 static void exec() {
154 templatedProp<T>("gives up if not enough unique elements can be generated",
155 [](const Random &random) {
156 const auto gen = Factory::template makeGen<T>(gen::just(0));
157 auto r = random;
158 while (true) {
159 try {
160 gen(r.split(), kNominalSize).value();
161 } catch (const GenerationFailure &) {
162 RC_SUCCEED("Threw GenerationFailure");
163 }
164 }
165 });
166
167 templatedProp<T>(
168 "increases size when enough unique elements cannot be generated",
169 [](const GenParams ¶ms) {
170 RC_PRE(params.size > 0);
171 const auto gen = Factory::template makeGen<T>(genSize());
172 auto r = params.random;
173 try {
174 gen(r.split(), params.size).value();
175 } catch (const GenerationFailure &e) {
176 RC_FAIL(std::string("Threw GenerationFailure: ") + e.what());
177 }
178 });
179 }
180 };
181
182 struct SequenceProperties {
183 template <typename T>
execSequenceProperties184 static void exec() {
185 templatedProp<T>(
186 "finds minimum where a particular range of consecutive elements"
187 " must be removed at once",
188 [](const Random &random) {
189 int size = *gen::inRange<int>(0, 50);
190 const auto shrinkable =
191 gen::container<T>(gen::arbitrary<int>())(random, size);
192 const auto value = shrinkable.value();
193
194 const auto i1 = *gen::inRange<std::size_t>(0, containerSize(value));
195 const auto i2 =
196 *gen::distinctFrom(
197 gen::inRange<std::size_t>(0, containerSize(value)), i1);
198 // TODO range generator
199 const auto il = std::min(i1, i2);
200 const auto ir = std::max(i1, i2);
201 std::array<int, 2> values{*std::next(begin(value), il),
202 *std::next(begin(value), ir)};
203
204 const auto pred = [&](const T &x) {
205 return std::search(begin(x), end(x), begin(values), end(values)) !=
206 end(x);
207 };
208
209 const auto result = shrinkable::findLocalMin(shrinkable, pred);
210 const T expected{values[0], values[1]};
211 RC_ASSERT(result.first == expected);
212 });
213
214 templatedProp<T>(
215 "finds minimum where some elements need to be larger than some value",
216 [](const Random &random) {
217 int size = *gen::inRange<int>(0, 50);
218
219 const int target = *gen::inRange<int>(0, 10);
220 const auto gen = gen::container<T>(gen::arbitrary<int>());
221 const auto result = searchGen(random,
222 size,
223 gen,
224 [=](const T &x) {
225 int count = 0;
226 for (const auto &e : x) {
227 if (e >= target) {
228 count++;
229 if (count >= 2) {
230 return true;
231 }
232 }
233 }
234
235 return false;
236 });
237
238 const T expected{target, target};
239 RC_ASSERT(result == expected);
240 });
241 }
242 };
243
244 } // namespace test
245 } // namespace rc
246