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