1 #include <catch2/catch.hpp>
2 
3 #include "util/Util.h"
4 #include "util/Logger.h"
5 #include "util/AppleOrange.h"
6 #include "util/ThrowOnCopy.h"
7 
8 #include "rapidcheck/detail/Variant.h"
9 
10 using namespace rc;
11 using namespace rc::test;
12 using namespace rc::detail;
13 
14 namespace {
15 
16 template <std::size_t N>
17 struct X {
X__anon05005cf40111::X18   X(const std::string &x)
19       : value(x) {}
20 
21   // Put some extra junk here so that different types have different layout.
22   char extra[N];
23   std::string value;
24 };
25 
26 template <std::size_t N>
operator ==(const X<N> & x1,const X<N> & x2)27 bool operator==(const X<N> &x1, const X<N> &x2) {
28   return x1.value == x2.value;
29 }
30 
31 using A = X<5>;
32 using B = X<10>;
33 using C = X<15>;
34 
35 using ABC = Variant<Logger, ThrowOnCopy, A, B, C>;
36 
37 } // namespace
38 
39 TEST_CASE("Variant") {
40   ABC va(A("A"));
41   ABC vb(B("B"));
42   ABC vc(C("C"));
43 
44   SECTION("universal constructor") {
45     SECTION("rvalue") {
46       ABC v(Logger("foobar"));
47       REQUIRE(v.is<Logger>());
48       REQUIRE(v.get<Logger>().id == "foobar");
49       REQUIRE(v.get<Logger>().numberOf("copy") == 0);
50     }
51 
52     SECTION("lvalue") {
53       Logger logger("foobar");
54       ABC v(logger);
55       REQUIRE(v.is<Logger>());
56       REQUIRE(v.get<Logger>().id == "foobar");
57       REQUIRE(v.get<Logger>().numberOf("copy") == 1);
58     }
59   }
60 
61   SECTION("value assignment") {
62     SECTION("from same type") {
63       SECTION("rvalue") {
64         ABC v(Logger("bar"));
65         v = Logger("foo");
66         REQUIRE(v.is<Logger>());
67         REQUIRE(v.get<Logger>().id == "foo");
68         REQUIRE(v.get<Logger>().numberOf("copy") == 0);
69         REQUIRE(v.get<Logger>().numberOf("move") == 1);
70         REQUIRE(v.get<Logger>().numberOf("move assigned") == 1);
71       }
72 
73       SECTION("lvalue") {
74         ABC v(Logger("bar"));
75         Logger foo("foo");
76         v = foo;
77         REQUIRE(v.is<Logger>());
78         REQUIRE(v.get<Logger>().id == "foo");
79         REQUIRE(v.get<Logger>().numberOf("copy") == 1);
80         REQUIRE(v.get<Logger>().numberOf("copy assigned") == 1);
81       }
82     }
83 
84     SECTION("from different type") {
85       SECTION("rvalue") {
86         ABC v(va);
87         v = Logger("foo");
88         REQUIRE(v.is<Logger>());
89         REQUIRE(v.get<Logger>().id == "foo");
90         REQUIRE(v.get<Logger>().numberOf("copy") == 0);
91         REQUIRE(v.get<Logger>().numberOf("move") == 1);
92         REQUIRE(v.get<Logger>().numberOf("move constructed") == 1);
93       }
94 
95       SECTION("lvalue") {
96         ABC v(va);
97         Logger foo("foo");
98         v = foo;
99         REQUIRE(v.is<Logger>());
100         REQUIRE(v.get<Logger>().id == "foo");
101         REQUIRE(v.get<Logger>().numberOf("copy") == 1);
102         REQUIRE(v.get<Logger>().numberOf("copy constructed") == 1);
103       }
104     }
105   }
106 
107   SECTION("copy constructor") {
108     ABC v1(Logger("foobar"));
109     ABC v2(v1);
110     REQUIRE(v2.is<Logger>());
111     REQUIRE(v2.get<Logger>().id == "foobar");
112     REQUIRE(v2.get<Logger>().numberOf("copy") == 1);
113   }
114 
115   SECTION("move constructor") {
116     ABC v1(Logger("foobar"));
117     ABC v2(std::move(v1));
118     REQUIRE(v2.is<Logger>());
119     REQUIRE(v2.get<Logger>().id == "foobar");
120     REQUIRE(v2.get<Logger>().numberOf("copy") == 0);
121   }
122 
123   SECTION("copy assignment") {
124     SECTION("from same type") {
125       SECTION("assigns object") {
126         ABC v1(Logger("foo"));
127         ABC v2(Logger("bar"));
128         v2 = v1;
129         REQUIRE(v2.is<Logger>());
130         REQUIRE(v2.get<Logger>().id == "foo");
131         REQUIRE(v2.get<Logger>().numberOf("copy") == 1);
132         REQUIRE(v2.get<Logger>().numberOf("copy assigned") == 1);
133       }
134 
135       SECTION("throwing leaves both values unchanged") {
136         ABC v1(ThrowOnCopy("foo"));
137         ABC v2(ThrowOnCopy("bar"));
138         try {
139           v1 = v2;
140         } catch (...) {
141         }
142         REQUIRE(v1.is<ThrowOnCopy>());
143         REQUIRE(v1.get<ThrowOnCopy>().value == "foo");
144         REQUIRE(v2.is<ThrowOnCopy>());
145         REQUIRE(v2.get<ThrowOnCopy>().value == "bar");
146       }
147     }
148 
149     SECTION("from different type") {
150       SECTION("constructs object") {
151         ABC v1(Logger("foo"));
152         ABC v2(va);
153         v2 = v1;
154         REQUIRE(v2.is<Logger>());
155         REQUIRE(v2.get<Logger>().id == "foo");
156         REQUIRE(v2.get<Logger>().numberOf("copy") == 1);
157         REQUIRE(v2.get<Logger>().numberOf("copy constructed") == 1);
158       }
159 
160       SECTION("self assignment leaves value unchanged") {
161         ABC v(Logger("foo"));
162         auto &ref = v;
163         v = ref;
164         REQUIRE(v.is<Logger>());
165         REQUIRE(v.get<Logger>().id == "foo");
166       }
167 
168       SECTION("throwing leaves both values unchanged") {
169         ABC v1(A("foo"));
170         ABC v2(ThrowOnCopy("bar"));
171         try {
172           v1 = v2;
173         } catch (...) {
174         }
175         REQUIRE(v1.is<A>());
176         REQUIRE(v1.get<A>().value == "foo");
177         REQUIRE(v2.is<ThrowOnCopy>());
178         REQUIRE(v2.get<ThrowOnCopy>().value == "bar");
179       }
180     }
181   }
182 
183   SECTION("move assignment") {
184     SECTION("from same type") {
185       ABC v1(Logger("foo"));
186       ABC v2(Logger("bar"));
187       v2 = std::move(v1);
188       REQUIRE(v2.is<Logger>());
189       REQUIRE(v2.get<Logger>().id == "foo");
190       REQUIRE(v2.get<Logger>().numberOf("copy") == 0);
191       REQUIRE(v2.get<Logger>().numberOf("move constructed") == 1);
192       REQUIRE(v2.get<Logger>().numberOf("move assigned") == 1);
193     }
194 
195     SECTION("from different type") {
196       ABC v1(Logger("foo"));
197       ABC v2(va);
198       v2 = std::move(v1);
199       REQUIRE(v2.is<Logger>());
200       REQUIRE(v2.get<Logger>().id == "foo");
201       REQUIRE(v2.get<Logger>().numberOf("copy") == 0);
202       REQUIRE(v2.get<Logger>().numberOf("move constructed") == 2);
203     }
204   }
205 
206   SECTION("is") {
207     SECTION("returns true if the value has the given type") {
208       REQUIRE(va.is<A>());
209       REQUIRE(vb.is<B>());
210       REQUIRE(vc.is<C>());
211     }
212 
213     SECTION("returns false if value does not have the given type") {
214       REQUIRE(!va.is<B>());
215       REQUIRE(!va.is<C>());
216 
217       REQUIRE(!vb.is<A>());
218       REQUIRE(!vb.is<C>());
219 
220       REQUIRE(!vc.is<A>());
221       REQUIRE(!vc.is<B>());
222     }
223   }
224 
225   SECTION("get") {
226     SECTION("returns rvalue reference for rvalues") {
227       ABC v(Logger("foo"));
228       const auto logger = std::move(v).get<Logger>();
229       REQUIRE(logger.numberOf("copy") == 0);
230     }
231   }
232 
233   SECTION("match") {
234     A a("AAA");
235     B b("BBB");
236     C c("CCC");
237 
238     SECTION("returns true if the value has the given type") {
239       REQUIRE(va.match(a));
240       REQUIRE(vb.match(b));
241       REQUIRE(vc.match(c));
242     }
243 
244     SECTION("returns false if value does not have the given type") {
245       REQUIRE(!va.match(b));
246       REQUIRE(!va.match(c));
247 
248       REQUIRE(!vb.match(a));
249       REQUIRE(!vb.match(c));
250 
251       REQUIRE(!vc.match(a));
252       REQUIRE(!vc.match(b));
253     }
254 
255     SECTION("leaves given value untouched if no match") {
256       A aa(a);
257       vb.match(aa);
258       vc.match(aa);
259       REQUIRE(a.value == aa.value);
260 
261       B bb(b);
262       va.match(bb);
263       vc.match(bb);
264       REQUIRE(b.value == bb.value);
265 
266       C cc(c);
267       va.match(cc);
268       vb.match(cc);
269       REQUIRE(c.value == cc.value);
270     }
271 
272     SECTION("sets the value to the contained value on match") {
273       va.match(a);
274       REQUIRE(a.value == "A");
275       vb.match(b);
276       REQUIRE(b.value == "B");
277       vc.match(c);
278       REQUIRE(c.value == "C");
279     }
280   }
281 
282   SECTION("operator==/operator!=") {
283     SECTION("equals if contained values are equal") {
284       REQUIRE(ABC(A("a")) == ABC(A("a")));
285       REQUIRE(ABC(B("b")) == ABC(B("b")));
286       REQUIRE(ABC(C("c")) == ABC(C("c")));
287 
288       REQUIRE_FALSE(ABC(A("a")) != ABC(A("a")));
289       REQUIRE_FALSE(ABC(B("b")) != ABC(B("b")));
290       REQUIRE_FALSE(ABC(C("c")) != ABC(C("c")));
291     }
292 
293     SECTION("not equals if contained values are not equal") {
294       REQUIRE_FALSE(ABC(A("a")) == ABC(A("ax")));
295       REQUIRE_FALSE(ABC(A("ax")) == ABC(A("a")));
296       REQUIRE_FALSE(ABC(B("b")) == ABC(B("bx")));
297       REQUIRE_FALSE(ABC(B("bx")) == ABC(B("b")));
298       REQUIRE_FALSE(ABC(C("c")) == ABC(C("cx")));
299       REQUIRE_FALSE(ABC(C("cx")) == ABC(C("c")));
300 
301       REQUIRE(ABC(A("a")) != ABC(A("ax")));
302       REQUIRE(ABC(A("ax")) != ABC(A("a")));
303       REQUIRE(ABC(B("b")) != ABC(B("bx")));
304       REQUIRE(ABC(B("bx")) != ABC(B("b")));
305       REQUIRE(ABC(C("c")) != ABC(C("cx")));
306       REQUIRE(ABC(C("cx")) != ABC(C("c")));
307     }
308 
309     SECTION("not equals if contained values are not of same type") {
310       REQUIRE_FALSE(ABC(A("a")) == ABC(B("a")));
311       REQUIRE_FALSE(ABC(B("a")) == ABC(A("a")));
312       REQUIRE(ABC(A("a")) != ABC(B("a")));
313       REQUIRE(ABC(B("a")) != ABC(A("a")));
314 
315       // Not even if types are normally equatable
316       using Orapple = Variant<Apple, Orange>;
317       REQUIRE_FALSE(Orapple(Apple("foo")) == Orapple(Orange("foo")));
318       REQUIRE_FALSE(Orapple(Orange("foo")) == Orapple(Apple("foo")));
319       REQUIRE(Orapple(Apple("foo")) != Orapple(Orange("foo")));
320       REQUIRE(Orapple(Orange("foo")) != Orapple(Apple("foo")));
321     }
322   }
323 
324   SECTION("operator<<(std::ostream)") {
325     SECTION("uses the contained types implementations") {
326       Variant<std::string, int> v(std::string("foobar"));
327       std::ostringstream str;
328       str << v;
329       REQUIRE(str.str() == "foobar");
330 
331       std::ostringstream num;
332       v = 1337;
333       num << v;
334       REQUIRE(num.str() == "1337");
335     }
336   }
337 }
338