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 &params) { 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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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 &params) {
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