1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <memory>
6 #include <type_traits>
7 
8 #include <lib/fit/optional.h>
9 #include <unittest/unittest.h>
10 
11 #include "unittest_utils.h"
12 
13 namespace {
14 
15 template <bool define_assignment_operators>
16 struct base {};
17 template <>
18 struct base<false> {
19     base& operator=(const base& other) = delete;
20     base& operator=(base&& other) = delete;
21 };
22 
23 template <bool define_assignment_operators>
24 struct slot : base<define_assignment_operators> {
slot__anon7bbb189e0111::slot25     slot(int value = 0)
26         : value(value) {
27         balance++;
28     }
slot__anon7bbb189e0111::slot29     slot(const slot& other)
30         : value(other.value) {
31         balance++;
32     }
slot__anon7bbb189e0111::slot33     slot(slot&& other)
34         : value(other.value) {
35         balance++;
36     }
37 
~slot__anon7bbb189e0111::slot38     ~slot() {
39         ASSERT_CRITICAL(balance > 0);
40         ASSERT_CRITICAL(value != -1);
41         value = -1; // sentinel to catch double-delete
42         balance--;
43     }
44 
45     static int balance; // net constructor/destructor pairings
46     int value;
47 
get__anon7bbb189e0111::slot48     int get() const { return value; }
increment__anon7bbb189e0111::slot49     int increment() { return ++value; }
50 
51     slot& operator=(const slot& other) = default;
52     slot& operator=(slot&& other) = default;
53 
operator ==__anon7bbb189e0111::slot54     bool operator==(const slot& other) const { return value == other.value; }
operator !=__anon7bbb189e0111::slot55     bool operator!=(const slot& other) const { return value != other.value; }
56 };
57 template <>
58 int slot<false>::balance = 0;
59 template <>
60 int slot<true>::balance = 0;
61 
62 // Test optional::value_type.
63 static_assert(std::is_same<int, fit::optional<int>::value_type>::value, "");
64 
65 // TODO(US-90): Unfortunately fit::optional is not a literal type unlike
66 // std::optional so expressions involving fit::optional are not constexpr.
67 // Once we fix that, we should add static asserts to check cases where
68 // fit::optional wraps a literal type.
69 
70 template <typename T>
construct_without_value()71 bool construct_without_value() {
72     BEGIN_TEST;
73 
74     fit::optional<T> opt;
75     EXPECT_FALSE(opt.has_value());
76     EXPECT_FALSE(!!opt);
77 
78     EXPECT_EQ(42, opt.value_or(T{42}).value);
79 
80     opt.reset();
81     EXPECT_FALSE(opt.has_value());
82 
83     END_TEST;
84 }
85 
86 template <typename T>
construct_with_value()87 bool construct_with_value() {
88     BEGIN_TEST;
89 
90     fit::optional<T> opt(T{42});
91     EXPECT_TRUE(opt.has_value());
92     EXPECT_TRUE(!!opt);
93 
94     EXPECT_EQ(42, opt.value().value);
95     EXPECT_EQ(42, opt.value_or(T{55}).value);
96 
97     EXPECT_EQ(42, opt->get());
98     EXPECT_EQ(43, opt->increment());
99     EXPECT_EQ(43, opt->get());
100 
101     opt.reset();
102     EXPECT_FALSE(opt.has_value());
103 
104     END_TEST;
105 }
106 
107 template <typename T>
construct_copy()108 bool construct_copy() {
109     BEGIN_TEST;
110 
111     fit::optional<T> a(T{42});
112     fit::optional<T> b(a);
113     fit::optional<T> c;
114     fit::optional<T> d(c);
115     EXPECT_TRUE(a.has_value());
116     EXPECT_EQ(42, a.value().value);
117     EXPECT_TRUE(b.has_value());
118     EXPECT_EQ(42, b.value().value);
119     EXPECT_FALSE(c.has_value());
120     EXPECT_FALSE(d.has_value());
121 
122     END_TEST;
123 }
124 
125 template <typename T>
construct_move()126 bool construct_move() {
127     BEGIN_TEST;
128 
129     fit::optional<T> a(T{42});
130     fit::optional<T> b(std::move(a));
131     fit::optional<T> c;
132     fit::optional<T> d(std::move(c));
133     EXPECT_FALSE(a.has_value());
134     EXPECT_TRUE(b.has_value());
135     EXPECT_EQ(42, b.value().value);
136     EXPECT_FALSE(c.has_value());
137     EXPECT_FALSE(d.has_value());
138 
139     END_TEST;
140 }
141 
142 template <typename T>
accessors()143 bool accessors() {
144     BEGIN_TEST;
145 
146     fit::optional<T> a(T{42});
147     T& value = a.value();
148     EXPECT_EQ(42, value.value);
149 
150     const T& const_value = const_cast<const decltype(a)&>(a).value();
151     EXPECT_EQ(42, const_value.value);
152 
153     T rvalue = fit::optional<T>(T{42}).value();
154     EXPECT_EQ(42, rvalue.value);
155 
156     T const_rvalue = const_cast<const fit::optional<T>&&>(
157                          fit::optional<T>(T{42}))
158                          .value();
159     EXPECT_EQ(42, const_rvalue.value);
160 
161     END_TEST;
162 }
163 
164 template <typename T>
assign()165 bool assign() {
166     BEGIN_TEST;
167 
168     fit::optional<T> a(T{42});
169     EXPECT_TRUE(a.has_value());
170     EXPECT_EQ(42, a.value().value);
171 
172     a = T{99};
173     EXPECT_TRUE(a.has_value());
174     EXPECT_EQ(99, a.value().value);
175 
176     a.reset();
177     EXPECT_FALSE(a.has_value());
178 
179     a = T{55};
180     EXPECT_TRUE(a.has_value());
181     EXPECT_EQ(55, a.value().value);
182 
183     a = fit::nullopt;
184     EXPECT_FALSE(a.has_value());
185 
186     END_TEST;
187 }
188 
189 template <typename T>
assign_copy()190 bool assign_copy() {
191     BEGIN_TEST;
192 
193     fit::optional<T> a(T{42});
194     fit::optional<T> b(T{55});
195     fit::optional<T> c;
196     EXPECT_TRUE(a.has_value());
197     EXPECT_EQ(42, a.value().value);
198     EXPECT_TRUE(b.has_value());
199     EXPECT_EQ(55, b.value().value);
200     EXPECT_FALSE(c.has_value());
201 
202     a = b;
203     EXPECT_TRUE(a.has_value());
204     EXPECT_EQ(55, b.value().value);
205     EXPECT_TRUE(b.has_value());
206     EXPECT_EQ(55, b.value().value);
207 
208     b = c;
209     EXPECT_FALSE(b.has_value());
210     EXPECT_FALSE(c.has_value());
211 
212     b = a;
213     EXPECT_TRUE(b.has_value());
214     EXPECT_EQ(55, b.value().value);
215     EXPECT_TRUE(a.has_value());
216     EXPECT_EQ(55, b.value().value);
217 
218 #ifdef __clang__
219 #pragma clang diagnostic push
220 #pragma clang diagnostic ignored "-Wself-assign-overloaded"
221 #endif
222 
223     b = b;
224     EXPECT_TRUE(b.has_value());
225     EXPECT_EQ(55, b.value().value);
226 
227     c = c;
228     EXPECT_FALSE(c.has_value());
229 
230 #ifdef __clang__
231 #pragma clang diagnostic pop
232 #endif
233 
234     END_TEST;
235 }
236 
237 template <typename T>
assign_move()238 bool assign_move() {
239     BEGIN_TEST;
240 
241     fit::optional<T> a(T{42});
242     fit::optional<T> b(T{55});
243     fit::optional<T> c;
244     EXPECT_TRUE(a.has_value());
245     EXPECT_EQ(42, a.value().value);
246     EXPECT_TRUE(b.has_value());
247     EXPECT_EQ(55, b.value().value);
248     EXPECT_FALSE(c.has_value());
249 
250     a = std::move(b);
251     EXPECT_TRUE(a.has_value());
252     EXPECT_EQ(55, a.value().value);
253     EXPECT_FALSE(b.has_value());
254 
255     b = std::move(c);
256     EXPECT_FALSE(b.has_value());
257     EXPECT_FALSE(c.has_value());
258 
259     c = std::move(b);
260     EXPECT_FALSE(c.has_value());
261     EXPECT_FALSE(b.has_value());
262 
263     b = std::move(a);
264     EXPECT_TRUE(b.has_value());
265     EXPECT_EQ(55, b.value().value);
266     EXPECT_FALSE(a.has_value());
267 
268     b = std::move(b);
269     EXPECT_TRUE(b.has_value());
270     EXPECT_EQ(55, b.value().value);
271 
272     a = std::move(a);
273     EXPECT_FALSE(a.has_value());
274 
275     END_TEST;
276 }
277 
278 template <typename T>
emplace()279 bool emplace() {
280     BEGIN_TEST;
281 
282     fit::optional<T> a;
283     EXPECT_EQ(55, a.emplace(55).value);
284     EXPECT_TRUE(a.has_value());
285     EXPECT_EQ(55, a.value().value);
286 
287     fit::optional<T> b(T{42});
288     EXPECT_EQ(66, b.emplace(66).value);
289     EXPECT_TRUE(b.has_value());
290     EXPECT_EQ(66, b.value().value);
291 
292     END_TEST;
293 }
294 
295 template <typename T>
invoke()296 bool invoke() {
297     BEGIN_TEST;
298 
299     fit::optional<T> a(T{42});
300     EXPECT_EQ(42, a->get());
301     EXPECT_EQ(43, a->increment());
302     EXPECT_EQ(43, (*a).value);
303 
304     END_TEST;
305 }
306 
307 template <typename T>
comparisons()308 bool comparisons() {
309     BEGIN_TEST;
310 
311     fit::optional<T> a(T{42});
312     fit::optional<T> b(T{55});
313     fit::optional<T> c(T{42});
314     fit::optional<T> d;
315     fit::optional<T> e;
316 
317     EXPECT_FALSE(a == b);
318     EXPECT_TRUE(a == c);
319     EXPECT_FALSE(a == d);
320     EXPECT_TRUE(d == e);
321     EXPECT_FALSE(d == a);
322 
323     EXPECT_FALSE(a == fit::nullopt);
324     EXPECT_FALSE(fit::nullopt == a);
325     EXPECT_TRUE(a == T{42});
326     EXPECT_TRUE(T{42} == a);
327     EXPECT_FALSE(a == T{55});
328     EXPECT_FALSE(T{55} == a);
329     EXPECT_FALSE(d == T{42});
330     EXPECT_FALSE(T{42} == d);
331     EXPECT_TRUE(d == fit::nullopt);
332     EXPECT_TRUE(fit::nullopt == d);
333 
334     EXPECT_TRUE(a != b);
335     EXPECT_FALSE(a != c);
336     EXPECT_TRUE(a != d);
337     EXPECT_FALSE(d != e);
338     EXPECT_TRUE(d != a);
339 
340     EXPECT_TRUE(a != fit::nullopt);
341     EXPECT_TRUE(fit::nullopt != a);
342     EXPECT_FALSE(a != T{42});
343     EXPECT_FALSE(T{42} != a);
344     EXPECT_TRUE(a != T{55});
345     EXPECT_TRUE(T{55} != a);
346     EXPECT_TRUE(d != T{42});
347     EXPECT_TRUE(T{42} != d);
348     EXPECT_FALSE(d != fit::nullopt);
349     EXPECT_FALSE(fit::nullopt != d);
350 
351     END_TEST;
352 }
353 
354 template <typename T>
swapping()355 bool swapping() {
356     BEGIN_TEST;
357 
358     fit::optional<T> a(T{42});
359     fit::optional<T> b(T{55});
360     fit::optional<T> c;
361     fit::optional<T> d;
362 
363     swap(a, b);
364     EXPECT_TRUE(a.has_value());
365     EXPECT_EQ(55, a.value().value);
366     EXPECT_TRUE(b.has_value());
367     EXPECT_EQ(42, b.value().value);
368 
369     swap(a, c);
370     EXPECT_FALSE(a.has_value());
371     EXPECT_TRUE(c.has_value());
372     EXPECT_EQ(55, c.value().value);
373 
374     swap(d, c);
375     EXPECT_FALSE(c.has_value());
376     EXPECT_TRUE(d.has_value());
377     EXPECT_EQ(55, d.value().value);
378 
379     swap(c, a);
380     EXPECT_FALSE(c.has_value());
381     EXPECT_FALSE(a.has_value());
382 
383     swap(a, a);
384     EXPECT_FALSE(a.has_value());
385 
386     swap(d, d);
387     EXPECT_TRUE(d.has_value());
388     EXPECT_EQ(55, d.value().value);
389 
390     END_TEST;
391 }
392 
393 template <typename T>
balance()394 bool balance() {
395     BEGIN_TEST;
396 
397     EXPECT_EQ(0, T::balance);
398 
399     END_TEST;
400 }
401 
402 } // namespace
403 
404 BEGIN_TEST_CASE(optional_tests)
405 RUN_TEST(construct_without_value<slot<false>>)
406 RUN_TEST(construct_without_value<slot<true>>)
407 RUN_TEST(construct_with_value<slot<false>>)
408 RUN_TEST(construct_with_value<slot<true>>)
409 RUN_TEST(construct_copy<slot<false>>)
410 RUN_TEST(construct_copy<slot<true>>)
411 RUN_TEST(construct_move<slot<false>>)
412 RUN_TEST(construct_move<slot<true>>)
413 RUN_TEST(accessors<slot<false>>)
414 RUN_TEST(accessors<slot<true>>)
415 RUN_TEST(assign<slot<false>>)
416 RUN_TEST(assign<slot<true>>)
417 RUN_TEST(assign_copy<slot<false>>)
418 RUN_TEST(assign_copy<slot<true>>)
419 RUN_TEST(assign_move<slot<false>>)
420 RUN_TEST(assign_move<slot<true>>)
421 RUN_TEST(emplace<slot<false>>)
422 RUN_TEST(emplace<slot<true>>)
423 RUN_TEST(invoke<slot<false>>)
424 RUN_TEST(invoke<slot<true>>)
425 RUN_TEST(comparisons<slot<false>>)
426 RUN_TEST(comparisons<slot<true>>)
427 RUN_TEST(swapping<slot<false>>)
428 RUN_TEST(swapping<slot<true>>)
429 RUN_TEST(balance<slot<false>>)
430 RUN_TEST(balance<slot<true>>)
431 END_TEST_CASE(optional_tests)
432