1/*
2 * Copyright 2014-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <folly/Try.h>
18
19#include <glog/logging.h>
20
21#include <folly/Memory.h>
22#include <folly/Traits.h>
23#include <folly/portability/GTest.h>
24
25using namespace folly;
26
27namespace {
28
29class A {
30 public:
31 explicit A(int x) : x_(x) {}
32
33 int x() const {
34 return x_;
35 }
36
37 private:
38 int x_;
39};
40
41template <bool Nothrow>
42class HasCtors {
43 public:
44 explicit HasCtors(int) noexcept(Nothrow) {}
45 HasCtors(HasCtors&&) noexcept(Nothrow) {}
46 HasCtors& operator=(HasCtors&&) noexcept(Nothrow) {}
47 HasCtors(HasCtors const&) noexcept(Nothrow) {}
48 HasCtors& operator=(HasCtors const&) noexcept(Nothrow) {}
49};
50
51class MoveConstructOnly {
52 public:
53 MoveConstructOnly() = default;
54 MoveConstructOnly(const MoveConstructOnly&) = delete;
55 MoveConstructOnly(MoveConstructOnly&&) = default;
56};
57
58class MutableContainer {
59 public:
60 mutable MoveConstructOnly val;
61};
62} // namespace
63
64TEST(Try, basic) {
65 A a(5);
66 Try<A> t_a(std::move(a));
67
68 Try<Unit> t_void;
69
70 EXPECT_EQ(5, t_a.value().x());
71}
72
73TEST(Try, in_place) {
74 Try<A> t_a(in_place, 5);
75
76 EXPECT_EQ(5, t_a.value().x());
77}
78
79TEST(Try, in_place_nested) {
80 Try<Try<A>> t_t_a(in_place, in_place, 5);
81
82 EXPECT_EQ(5, t_t_a.value().value().x());
83}
84
85TEST(Try, assignmentWithThrowingCopyConstructor) {
86 struct MyException : std::exception {};
87 struct ThrowingCopyConstructor {
88 int& counter_;
89 explicit ThrowingCopyConstructor(int& counter) : counter_(counter) {
90 ++counter_;
91 }
92
93 [[noreturn]] ThrowingCopyConstructor(
94 const ThrowingCopyConstructor& other) noexcept(false)
95 : counter_(other.counter_) {
96 throw MyException{};
97 }
98
99 ThrowingCopyConstructor& operator=(const ThrowingCopyConstructor&) = delete;
100
101 ~ThrowingCopyConstructor() {
102 --counter_;
103 }
104 };
105
106 int counter = 0;
107
108 {
109 Try<ThrowingCopyConstructor> t1{in_place, counter};
110 Try<ThrowingCopyConstructor> t2{in_place, counter};
111 EXPECT_EQ(2, counter);
112 EXPECT_THROW(t2 = t1, MyException);
113 EXPECT_EQ(1, counter);
114 EXPECT_FALSE(t2.hasValue());
115 EXPECT_TRUE(t1.hasValue());
116 }
117 EXPECT_EQ(0, counter);
118 {
119 Try<ThrowingCopyConstructor> t1{in_place, counter};
120 Try<ThrowingCopyConstructor> t2;
121 EXPECT_EQ(1, counter);
122 EXPECT_THROW(t2 = t1, MyException);
123 EXPECT_EQ(1, counter);
124 EXPECT_FALSE(t2.hasValue());
125 EXPECT_TRUE(t1.hasValue());
126 }
127 EXPECT_EQ(0, counter);
128}
129
130TEST(Try, assignmentWithThrowingMoveConstructor) {
131 struct MyException : std::exception {};
132 struct ThrowingMoveConstructor {
133 int& counter_;
134 explicit ThrowingMoveConstructor(int& counter) : counter_(counter) {
135 ++counter_;
136 }
137
138 [[noreturn]] ThrowingMoveConstructor(
139 ThrowingMoveConstructor&& other) noexcept(false)
140 : counter_(other.counter_) {
141 throw MyException{};
142 }
143
144 ThrowingMoveConstructor& operator=(ThrowingMoveConstructor&&) = delete;
145
146 ~ThrowingMoveConstructor() {
147 --counter_;
148 }
149 };
150
151 int counter = 0;
152
153 {
154 Try<ThrowingMoveConstructor> t1{in_place, counter};
155 Try<ThrowingMoveConstructor> t2{in_place, counter};
156 EXPECT_EQ(2, counter);
157 EXPECT_THROW(t2 = std::move(t1), MyException);
158 EXPECT_EQ(1, counter);
159 EXPECT_FALSE(t2.hasValue());
160 EXPECT_TRUE(t1.hasValue());
161 }
162 EXPECT_EQ(0, counter);
163 {
164 Try<ThrowingMoveConstructor> t1{in_place, counter};
165 Try<ThrowingMoveConstructor> t2;
166 EXPECT_EQ(1, counter);
167 EXPECT_THROW(t2 = std::move(t1), MyException);
168 EXPECT_EQ(1, counter);
169 EXPECT_FALSE(t2.hasValue());
170 EXPECT_TRUE(t1.hasValue());
171 }
172 EXPECT_EQ(0, counter);
173}
174
175TEST(Try, emplace) {
176 Try<A> t;
177 A& t_a = t.emplace(10);
178 EXPECT_TRUE(t.hasValue());
179 EXPECT_EQ(t_a.x(), 10);
180}
181
182TEST(Try, emplaceWithThrowingConstructor) {
183 struct MyException : std::exception {};
184 struct ThrowingConstructor {
185 explicit ThrowingConstructor(bool shouldThrow) {
186 if (shouldThrow) {
187 throw MyException{};
188 }
189 }
190 };
191
192 {
193 // Try constructing from empty state to new value and constructor throws.
194 Try<ThrowingConstructor> t;
195 EXPECT_FALSE(t.hasValue());
196 EXPECT_FALSE(t.hasException());
197 EXPECT_THROW(t.emplace(true), MyException);
198
199 EXPECT_FALSE(t.hasValue());
200 EXPECT_FALSE(t.hasException());
201 }
202
203 {
204 // Initialise to value, then re-emplace with throwing constructor.
205 // This should reset the object back to empty.
206 Try<ThrowingConstructor> t{in_place, false};
207 EXPECT_TRUE(t.hasValue());
208 EXPECT_THROW(t.emplace(true), MyException);
209 EXPECT_FALSE(t.hasValue());
210 EXPECT_FALSE(t.hasException());
211 }
212}
213
214TEST(Try, tryEmplace) {
215 Try<A> t;
216 A* a = tryEmplace(t, 10);
217 EXPECT_EQ(&t.value(), a);
218 EXPECT_TRUE(t.hasValue());
219 EXPECT_EQ(10, t.value().x());
220}
221
222TEST(Try, tryEmplaceWithThrowingConstructor) {
223 struct MyException : std::exception {};
224 struct NonInheritingException {};
225 struct ThrowingConstructor {
226 [[noreturn]] ThrowingConstructor() noexcept(false) {
227 throw NonInheritingException{}; // @nolint
228 }
229
230 explicit ThrowingConstructor(bool shouldThrow) {
231 if (shouldThrow) {
232 throw MyException{};
233 }
234 }
235 };
236
237 {
238 Try<ThrowingConstructor> t;
239 EXPECT_EQ(nullptr, tryEmplace(t, true));
240 EXPECT_TRUE(t.hasException());
241 EXPECT_NE(t.tryGetExceptionObject<MyException>(), nullptr);
242 }
243
244 {
245 Try<ThrowingConstructor> t;
246 EXPECT_EQ(nullptr, tryEmplace(t));
247 EXPECT_TRUE(t.hasException());
248 EXPECT_NE(t.tryGetExceptionObject<NonInheritingException>(), nullptr);
249 }
250
251 {
252 Try<ThrowingConstructor> t;
253 EXPECT_NE(nullptr, tryEmplace(t, false));
254 EXPECT_TRUE(t.hasValue());
255 EXPECT_EQ(nullptr, tryEmplace(t, true));
256 EXPECT_TRUE(t.hasException());
257 EXPECT_NE(t.tryGetExceptionObject<MyException>(), nullptr);
258 }
259}
260
261TEST(Try, emplaceVoidTry) {
262 struct MyException : std::exception {};
263 Try<void> t;
264 t.emplace();
265 EXPECT_TRUE(t.hasValue());
266 t.emplaceException(folly::in_place_type<MyException>);
267 EXPECT_FALSE(t.hasValue());
268 EXPECT_TRUE(t.hasException());
269 EXPECT_TRUE(t.hasException<MyException>());
270 t.emplace();
271 EXPECT_TRUE(t.hasValue());
272 EXPECT_FALSE(t.hasException());
273}
274
275TEST(Try, tryEmplaceVoidTry) {
276 struct MyException : std::exception {};
277 Try<void> t;
278 tryEmplace(t);
279 EXPECT_TRUE(t.hasValue());
280 t.emplaceException(folly::in_place_type<MyException>);
281 EXPECT_FALSE(t.hasValue());
282 EXPECT_TRUE(t.hasException());
283 EXPECT_TRUE(t.hasException<MyException>());
284 t.emplace();
285 EXPECT_TRUE(t.hasValue());
286 EXPECT_FALSE(t.hasException());
287}
288
289TEST(Try, tryEmplaceWith) {
290 Try<std::string> t;
291 tryEmplaceWith(t, [] { return "hello"; });
292 EXPECT_EQ("hello", t.value());
293}
294
295TEST(Try, tryEmplaceWithFunctionThrows) {
296 struct MyException : std::exception {};
297 Try<int> t;
298 tryEmplaceWith(t, []() -> int { throw MyException{}; });
299 EXPECT_TRUE(t.hasException());
300 EXPECT_TRUE(t.hasException<MyException>());
301}
302
303TEST(Try, tryEmplaceWithConstructorThrows) {
304 struct MyException : std::exception {};
305 struct ThrowingConstructor {
306 int value_;
307 explicit ThrowingConstructor(bool shouldThrow) noexcept(false) : value_(0) {
308 if (shouldThrow) {
309 throw MyException{};
310 }
311 }
312 };
313
314 Try<ThrowingConstructor> t;
315 tryEmplaceWith(t, [] { return false; });
316 EXPECT_TRUE(t.hasValue());
317 tryEmplaceWith(t, [] { return true; });
318 EXPECT_TRUE(t.hasException());
319 EXPECT_TRUE(t.hasException<MyException>());
320}
321
322TEST(Try, tryEmplaceWithVoidTry) {
323 Try<void> t;
324 bool hasRun = false;
325 tryEmplaceWith(t, [&] { hasRun = true; });
326 EXPECT_TRUE(t.hasValue());
327 EXPECT_TRUE(hasRun);
328
329 struct MyException : std::exception {};
330 tryEmplaceWith(t, [&] { throw MyException{}; });
331 EXPECT_TRUE(t.hasException());
332 EXPECT_TRUE(t.hasException<MyException>());
333}
334
335TEST(Try, nothrow) {
336 using F = HasCtors<false>;
337 using T = HasCtors<true>;
338
339 // default ctor
340 EXPECT_TRUE(std::is_nothrow_default_constructible<Try<F>>::value);
341 EXPECT_TRUE(std::is_nothrow_default_constructible<Try<T>>::value);
342 EXPECT_TRUE(std::is_nothrow_default_constructible<Try<void>>::value);
343
344 // inner ctor - no void
345 EXPECT_FALSE((std::is_nothrow_constructible<Try<F>, F&&>::value));
346 EXPECT_TRUE((std::is_nothrow_constructible<Try<T>, T&&>::value));
347 EXPECT_FALSE((std::is_nothrow_constructible<Try<F>, F const&>::value));
348 EXPECT_TRUE((std::is_nothrow_constructible<Try<T>, T const&>::value));
349
350 // emplacing ctor - no void
351 EXPECT_FALSE((std::is_nothrow_constructible<Try<F>, in_place_t, int>::value));
352 EXPECT_TRUE((std::is_nothrow_constructible<Try<T>, in_place_t, int>::value));
353
354 // copy/move ctor/assign
355 EXPECT_TRUE(std::is_nothrow_constructible<Try<void>>::value);
356 EXPECT_FALSE(std::is_nothrow_move_constructible<Try<F>>::value);
357 EXPECT_TRUE(std::is_nothrow_move_constructible<Try<T>>::value);
358 EXPECT_TRUE(std::is_nothrow_move_constructible<Try<void>>::value);
359 EXPECT_FALSE(std::is_nothrow_move_assignable<Try<F>>::value);
360 EXPECT_TRUE(std::is_nothrow_move_assignable<Try<T>>::value);
361 EXPECT_TRUE(std::is_nothrow_move_assignable<Try<void>>::value);
362 EXPECT_FALSE(std::is_nothrow_copy_constructible<Try<F>>::value);
363 EXPECT_TRUE(std::is_nothrow_copy_constructible<Try<T>>::value);
364 EXPECT_TRUE(std::is_nothrow_copy_constructible<Try<void>>::value);
365 EXPECT_FALSE(std::is_nothrow_copy_assignable<Try<F>>::value);
366 EXPECT_TRUE(std::is_nothrow_copy_assignable<Try<T>>::value);
367 EXPECT_TRUE(std::is_nothrow_copy_assignable<Try<void>>::value);
368
369 // conversion ctor - void to unit
370 EXPECT_TRUE((std::is_nothrow_constructible<Try<Unit>, Try<void>&&>::value));
371 EXPECT_TRUE(
372 (std::is_nothrow_constructible<Try<Unit>, Try<void> const&>::value));
373}
374
375TEST(Try, MoveDereference) {
376 auto ptr = std::make_unique<int>(1);
377 auto t = Try<std::unique_ptr<int>>{std::move(ptr)};
378 auto result = *std::move(t);
379 EXPECT_EQ(*result, 1);
380}
381
382TEST(Try, MoveConstRvalue) {
383 // tests to see if Try returns a const Rvalue, this is required in the case
384 // where for example MutableContainer has a mutable memebr that is move only
385 // and you want to fetch the value from the Try and move it into a member
386 {
387 const Try<MutableContainer> t{in_place};
388 auto val = MoveConstructOnly(std::move(t).value().val);
389 static_cast<void>(val);
390 }
391 {
392 const Try<MutableContainer> t{in_place};
393 auto val = (*(std::move(t))).val;
394 static_cast<void>(val);
395 }
396}
397
398TEST(Try, ValueOverloads) {
399 using ML = int&;
400 using MR = int&&;
401 using CL = const int&;
402 using CR = const int&&;
403
404 {
405 auto obj = Try<int>{};
406 using ActualML = decltype(obj.value());
407 using ActualMR = decltype(std::move(obj).value());
408 using ActualCL = decltype(as_const(obj).value());
409 using ActualCR = decltype(std::move(as_const(obj)).value());
410 EXPECT_TRUE((std::is_same<ML, ActualML>::value));
411 EXPECT_TRUE((std::is_same<MR, ActualMR>::value));
412 EXPECT_TRUE((std::is_same<CL, ActualCL>::value));
413 EXPECT_TRUE((std::is_same<CR, ActualCR>::value));
414 }
415
416 {
417 auto obj = Try<int>{3};
418 EXPECT_EQ(obj.value(), 3);
419 EXPECT_EQ(std::move(obj).value(), 3);
420 EXPECT_EQ(as_const(obj).value(), 3);
421 EXPECT_EQ(std::move(as_const(obj)).value(), 3);
422 }
423
424 {
425 auto obj = Try<int>{make_exception_wrapper<std::range_error>("oops")};
426 EXPECT_THROW(obj.value(), std::range_error);
427 EXPECT_THROW(std::move(obj.value()), std::range_error);
428 EXPECT_THROW(as_const(obj.value()), std::range_error);
429 EXPECT_THROW(std::move(as_const(obj.value())), std::range_error);
430 }
431}
432
433// Make sure we can copy Trys for copyable types
434TEST(Try, copy) {
435 Try<int> t;
436 auto t2 = t;
437}
438
439// But don't choke on move-only types
440TEST(Try, moveOnly) {
441 Try<std::unique_ptr<int>> t;
442 std::vector<Try<std::unique_ptr<int>>> v;
443 v.reserve(10);
444}
445
446TEST(Try, makeTryWith) {
447 auto func = []() { return std::make_unique<int>(1); };
448
449 auto result = makeTryWith(func);
450 EXPECT_TRUE(result.hasValue());
451 EXPECT_EQ(*result.value(), 1);
452}
453
454TEST(Try, makeTryWithThrow) {
455 auto func = []() -> std::unique_ptr<int> {
456 throw std::runtime_error("Runtime");
457 };
458
459 auto result = makeTryWith(func);
460 EXPECT_TRUE(result.hasException<std::runtime_error>());
461}
462
463TEST(Try, makeTryWithVoid) {
464 auto func = []() { return; };
465
466 auto result = makeTryWith(func);
467 EXPECT_TRUE(result.hasValue());
468}
469
470TEST(Try, makeTryWithVoidThrow) {
471 auto func = []() { throw std::runtime_error("Runtime"); };
472
473 auto result = makeTryWith(func);
474 EXPECT_TRUE(result.hasException<std::runtime_error>());
475}
476
477TEST(Try, exception) {
478 using ML = exception_wrapper&;
479 using MR = exception_wrapper&&;
480 using CL = exception_wrapper const&;
481 using CR = exception_wrapper const&&;
482
483 {
484 auto obj = Try<int>();
485 using ActualML = decltype(obj.exception());
486 using ActualMR = decltype(std::move(obj).exception());
487 using ActualCL = decltype(as_const(obj).exception());
488 using ActualCR = decltype(std::move(as_const(obj)).exception());
489 EXPECT_TRUE((std::is_same<ML, ActualML>::value));
490 EXPECT_TRUE((std::is_same<MR, ActualMR>::value));
491 EXPECT_TRUE((std::is_same<CL, ActualCL>::value));
492 EXPECT_TRUE((std::is_same<CR, ActualCR>::value));
493 }
494
495 {
496 auto obj = Try<int>(3);
497 EXPECT_THROW(obj.exception(), TryException);
498 EXPECT_THROW(std::move(obj).exception(), TryException);
499 EXPECT_THROW(as_const(obj).exception(), TryException);
500 EXPECT_THROW(std::move(as_const(obj)).exception(), TryException);
501 }
502
503 {
504 auto obj = Try<int>(make_exception_wrapper<int>(-3));
505 EXPECT_EQ(-3, *obj.exception().get_exception<int>());
506 EXPECT_EQ(-3, *std::move(obj).exception().get_exception<int>());
507 EXPECT_EQ(-3, *as_const(obj).exception().get_exception<int>());
508 EXPECT_EQ(-3, *std::move(as_const(obj)).exception().get_exception<int>());
509 }
510
511 {
512 auto obj = Try<void>();
513 using ActualML = decltype(obj.exception());
514 using ActualMR = decltype(std::move(obj).exception());
515 using ActualCL = decltype(as_const(obj).exception());
516 using ActualCR = decltype(std::move(as_const(obj)).exception());
517 EXPECT_TRUE((std::is_same<ML, ActualML>::value));
518 EXPECT_TRUE((std::is_same<MR, ActualMR>::value));
519 EXPECT_TRUE((std::is_same<CL, ActualCL>::value));
520 EXPECT_TRUE((std::is_same<CR, ActualCR>::value));
521 }
522
523 {
524 auto obj = Try<void>();
525 EXPECT_THROW(obj.exception(), TryException);
526 EXPECT_THROW(std::move(obj).exception(), TryException);
527 EXPECT_THROW(as_const(obj).exception(), TryException);
528 EXPECT_THROW(std::move(as_const(obj)).exception(), TryException);
529 }
530
531 {
532 auto obj = Try<void>(make_exception_wrapper<int>(-3));
533 EXPECT_EQ(-3, *obj.exception().get_exception<int>());
534 EXPECT_EQ(-3, *std::move(obj).exception().get_exception<int>());
535 EXPECT_EQ(-3, *as_const(obj).exception().get_exception<int>());
536 EXPECT_EQ(-3, *std::move(as_const(obj)).exception().get_exception<int>());
537 }
538}
539
540template <typename E>
541static E* get_exception(std::exception_ptr eptr) {
542 try {
543 std::rethrow_exception(eptr);
544 } catch (E& e) {
545 return &e;
546 } catch (...) {
547 return nullptr;
548 }
549}
550
551TEST(Try, tryGetExceptionObject) {
552 auto epexn = std::make_exception_ptr(std::range_error("oops"));
553 auto epnum = std::make_exception_ptr(17);
554
555 auto exn = CHECK_NOTNULL(get_exception<std::range_error>(epexn));
556 auto num = CHECK_NOTNULL(get_exception<int>(epnum));
557
558 {
559 auto t = Try<bool>(true);
560 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
561 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
562 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
563 }
564
565 {
566 auto t = Try<bool>(exception_wrapper(epexn, *exn));
567 EXPECT_EQ(exn, t.tryGetExceptionObject());
568 EXPECT_EQ(exn, t.tryGetExceptionObject<std::runtime_error>());
569 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
570 }
571
572 {
573 auto t = Try<bool>(exception_wrapper(epnum, *num));
574 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
575 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
576 EXPECT_EQ(num, t.tryGetExceptionObject<int>());
577 }
578
579 {
580 auto t = Try<void>();
581 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
582 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
583 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
584 }
585
586 {
587 auto t = Try<void>(exception_wrapper(epexn, *exn));
588 EXPECT_EQ(exn, t.tryGetExceptionObject());
589 EXPECT_EQ(exn, t.tryGetExceptionObject<std::runtime_error>());
590 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
591 }
592
593 {
594 auto t = Try<void>(exception_wrapper(epnum, *num));
595 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
596 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
597 EXPECT_EQ(num, t.tryGetExceptionObject<int>());
598 }
599
600 {
601 auto const t = Try<bool>(true);
602 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
603 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
604 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
605 }
606
607 {
608 auto const t = Try<bool>(exception_wrapper(epexn, *exn));
609 EXPECT_EQ(exn, t.tryGetExceptionObject());
610 EXPECT_EQ(exn, t.tryGetExceptionObject<std::runtime_error>());
611 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
612 }
613
614 {
615 auto const t = Try<bool>(exception_wrapper(epnum, *num));
616 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
617 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
618 EXPECT_EQ(num, t.tryGetExceptionObject<int>());
619 }
620
621 {
622 auto const t = Try<void>();
623 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
624 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
625 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
626 }
627
628 {
629 auto const t = Try<void>(exception_wrapper(epexn, *exn));
630 EXPECT_EQ(exn, t.tryGetExceptionObject());
631 EXPECT_EQ(exn, t.tryGetExceptionObject<std::runtime_error>());
632 EXPECT_EQ(nullptr, t.tryGetExceptionObject<int>());
633 }
634
635 {
636 auto const t = Try<void>(exception_wrapper(epnum, *num));
637 EXPECT_EQ(nullptr, t.tryGetExceptionObject());
638 EXPECT_EQ(nullptr, t.tryGetExceptionObject<std::runtime_error>());
639 EXPECT_EQ(num, t.tryGetExceptionObject<int>());
640 }
641}
642
643TEST(Try, withException) {
644 auto ew = make_exception_wrapper<std::range_error>("oops");
645
646 {
647 auto t = Try<bool>(true);
648 EXPECT_FALSE(t.withException<std::runtime_error>([](auto&) {}));
649 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
650 EXPECT_FALSE(t.withException([](std::runtime_error&) {}));
651 EXPECT_FALSE(t.withException([](std::logic_error&) {}));
652 }
653
654 {
655 auto t = Try<bool>(ew);
656 EXPECT_TRUE(t.withException<std::runtime_error>([](auto&) {}));
657 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
658 EXPECT_TRUE(t.withException([](std::runtime_error&) {}));
659 EXPECT_FALSE(t.withException([](std::logic_error&) {}));
660 }
661
662 {
663 auto t = Try<void>();
664 EXPECT_FALSE(t.withException<std::runtime_error>([](auto&) {}));
665 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
666 EXPECT_FALSE(t.withException([](std::runtime_error&) {}));
667 EXPECT_FALSE(t.withException([](std::logic_error&) {}));
668 }
669
670 {
671 auto t = Try<void>(ew);
672 EXPECT_TRUE(t.withException<std::runtime_error>([](auto&) {}));
673 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
674 EXPECT_TRUE(t.withException([](std::runtime_error&) {}));
675 EXPECT_FALSE(t.withException([](std::logic_error&) {}));
676 }
677
678 {
679 auto const t = Try<bool>(true);
680 EXPECT_FALSE(t.withException<std::runtime_error>([](auto&) {}));
681 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
682 EXPECT_FALSE(t.withException([](std::runtime_error const&) {}));
683 EXPECT_FALSE(t.withException([](std::logic_error const&) {}));
684 }
685
686 {
687 auto const t = Try<bool>(ew);
688 EXPECT_TRUE(t.withException<std::runtime_error>([](auto&) {}));
689 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
690 EXPECT_TRUE(t.withException([](std::runtime_error const&) {}));
691 EXPECT_FALSE(t.withException([](std::logic_error const&) {}));
692 }
693
694 {
695 auto const t = Try<void>();
696 EXPECT_FALSE(t.withException<std::runtime_error>([](auto&) {}));
697 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
698 EXPECT_FALSE(t.withException([](std::runtime_error const&) {}));
699 EXPECT_FALSE(t.withException([](std::logic_error const&) {}));
700 }
701
702 {
703 auto const t = Try<void>(ew);
704 EXPECT_TRUE(t.withException<std::runtime_error>([](auto&) {}));
705 EXPECT_FALSE(t.withException<std::logic_error>([](auto&) {}));
706 EXPECT_TRUE(t.withException([](std::runtime_error const&) {}));
707 EXPECT_FALSE(t.withException([](std::logic_error const&) {}));
708 }
709}
710
711TEST(Try, TestUnwrapTuple) {
712 auto original = std::make_tuple(Try<int>{1}, Try<int>{2});
713 EXPECT_EQ(std::make_tuple(1, 2), unwrapTryTuple(original));
714 EXPECT_EQ(std::make_tuple(1, 2), unwrapTryTuple(folly::copy(original)));
715 EXPECT_EQ(std::make_tuple(1, 2), unwrapTryTuple(folly::as_const(original)));
716}
717
718TEST(Try, TestUnwrapPair) {
719 auto original = std::make_pair(Try<int>{1}, Try<int>{2});
720 EXPECT_EQ(std::make_pair(1, 2), unwrapTryTuple(original));
721 EXPECT_EQ(std::make_pair(1, 2), unwrapTryTuple(folly::copy(original)));
722 EXPECT_EQ(std::make_pair(1, 2), unwrapTryTuple(folly::as_const(original)));
723}
724
725TEST(Try, TestUnwrapForward) {
726 using UPtr_t = std::unique_ptr<int>;
727 auto original = std::make_tuple(Try<UPtr_t>{std::make_unique<int>(1)});
728 auto unwrapped = unwrapTryTuple(std::move(original));
729 EXPECT_EQ(*std::get<0>(unwrapped), 1);
730}
731