//////////////////////////////////////////////////////////////////////////////// /// @brief test case for Futures/Try object /// /// @file /// /// DISCLAIMER /// /// Copyright 2018 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Simon Grätzer //////////////////////////////////////////////////////////////////////////////// #include "Futures/Try.h" #include "gtest/gtest.h" using namespace arangodb::futures; // from folly Utilities.h #if __cpp_lib_as_const || _MSC_VER /* using override */ using std::as_const; #else template constexpr T const& as_const(T& t) noexcept { return t; } template void as_const(T const&&) = delete; #endif namespace { class A { public: explicit A(int x) : x_(x) {} int x() const { return x_; } private: int x_; }; template class HasCtors { public: explicit HasCtors(int) noexcept(Nothrow) {} HasCtors(HasCtors&&) noexcept(Nothrow) {} HasCtors& operator=(HasCtors&&) noexcept(Nothrow) {} HasCtors(HasCtors const&) noexcept(Nothrow) {} HasCtors& operator=(HasCtors const&) noexcept(Nothrow) {} }; class MoveConstructOnly { public: MoveConstructOnly() = default; MoveConstructOnly(const MoveConstructOnly&) = delete; MoveConstructOnly(MoveConstructOnly&&) = default; }; class MutableContainer { public: mutable MoveConstructOnly val; }; } // namespace // ----------------------------------------------------------------------------- // --SECTION-- test suite // ----------------------------------------------------------------------------- TEST(FuturesTryTest, Basic) { A a(5); Try t_a(std::move(a)); // Try t_void; ASSERT_TRUE(5 == t_a.get().x()); } TEST(FuturesTryTest, in_place) { Try t_a(in_place, 5); ASSERT_TRUE(5 == t_a.get().x()); } TEST(FuturesTryTest, in_place_nested) { Try> t_t_a(in_place, in_place, 5); ASSERT_TRUE(5 == t_t_a.get().get().x()); } TEST(FuturesTryTest, assignment_with_throwing_ctor) { struct MyException : std::exception {}; struct ThrowingCopyConstructor { int& counter_; explicit ThrowingCopyConstructor(int& counter) : counter_(counter) { ++counter_; } [[noreturn]] ThrowingCopyConstructor(const ThrowingCopyConstructor& other) noexcept(false) : counter_(other.counter_) { throw MyException{}; } ThrowingCopyConstructor& operator=(const ThrowingCopyConstructor&) = delete; ~ThrowingCopyConstructor() { --counter_; } }; int counter = 0; { Try t1{in_place, counter}; Try t2{in_place, counter}; ASSERT_TRUE(2 == counter); EXPECT_ANY_THROW(t2 = t1); EXPECT_ANY_THROW(t2 = t1); ASSERT_TRUE(1 == counter); ASSERT_FALSE(t2.hasValue()); ASSERT_TRUE(t1.hasValue()); } ASSERT_TRUE(0 == counter); { Try t1{in_place, counter}; Try t2; ASSERT_TRUE(1 == counter); EXPECT_ANY_THROW(t2 = t1); ASSERT_TRUE(1 == counter); ASSERT_FALSE(t2.hasValue()); ASSERT_TRUE(t1.hasValue()); } ASSERT_TRUE(0 == counter); } // TODO assignmentWithThrowingMoveConstructor TEST(FuturesTryTest, emplace) { Try t; A& t_a = t.emplace(10); ASSERT_TRUE(t.hasValue()); ASSERT_TRUE(t_a.x() == 10); } TEST(FuturesTryTest, emplace_void) { struct MyException : std::exception {}; Try t; t.emplace(); ASSERT_TRUE(t.hasValue()); t.set_exception(MyException()); ASSERT_FALSE(t.hasValue()); ASSERT_TRUE(t.hasException()); t.emplace(); ASSERT_TRUE(t.hasValue()); ASSERT_FALSE(t.hasException()); } TEST(FuturesTryTest, MoveConstRvalue) { // tests to see if Try returns a const Rvalue, this is required in the case // where for example MutableContainer has a mutable member that is move only // and you want to fetch the value from the Try and move it into a member { const Try t{in_place}; auto val = MoveConstructOnly(std::move(t).get().val); static_cast(val); } { const Try t{in_place}; auto val = (*(std::move(t))).val; static_cast(val); } } // Make sure we can copy Trys for copyable types TEST(FuturesTryTest, copy) { Try t; auto t2 = t; } // But don't choke on move-only types TEST(FuturesTryTest, moveOnly) { Try> t; std::vector>> v; v.reserve(10); } TEST(FuturesTryTest, exception) { using ML = std::exception_ptr&; using MR = std::exception_ptr&&; using CL = std::exception_ptr const&; using CR = std::exception_ptr const&&; { auto obj = Try(); using ActualML = decltype(obj.exception()); using ActualMR = decltype(std::move(obj).exception()); using ActualCL = decltype(as_const(obj).exception()); using ActualCR = decltype(std::move(as_const(obj)).exception()); ASSERT_TRUE((std::is_same::value)); ASSERT_TRUE((std::is_same::value)); ASSERT_TRUE((std::is_same::value)); ASSERT_TRUE((std::is_same::value)); } { auto obj = Try(3); EXPECT_ANY_THROW(obj.exception()); EXPECT_ANY_THROW(std::move(obj).exception()); EXPECT_ANY_THROW(as_const(obj).exception()); EXPECT_ANY_THROW(std::move(as_const(obj)).exception()); } { auto obj = Try(std::make_exception_ptr(-3)); bool didThrow = false; try { obj.throwIfFailed(); } catch (int x) { didThrow = true; ASSERT_TRUE(x == -3); } catch (...) { FAIL() << "invalid exception type"; } ASSERT_TRUE(didThrow); /*EXPECT_EQ(-3, *obj.exception().get_exception()); EXPECT_EQ(-3, *std::move(obj).exception().get_exception()); EXPECT_EQ(-3, *as_const(obj).exception().get_exception()); EXPECT_EQ(-3, *std::move(as_const(obj)).exception().get_exception());*/ } { auto obj = Try(); using ActualML = decltype(obj.exception()); using ActualMR = decltype(std::move(obj).exception()); using ActualCL = decltype(as_const(obj).exception()); using ActualCR = decltype(std::move(as_const(obj)).exception()); ASSERT_TRUE((std::is_same::value)); ASSERT_TRUE((std::is_same::value)); ASSERT_TRUE((std::is_same::value)); ASSERT_TRUE((std::is_same::value)); } { auto obj = Try(); EXPECT_ANY_THROW(obj.exception()); EXPECT_ANY_THROW(std::move(obj).exception()); EXPECT_ANY_THROW(as_const(obj).exception()); EXPECT_ANY_THROW(std::move(as_const(obj)).exception()); } { auto obj = Try(std::make_exception_ptr(-3)); bool didThrow = false; try { obj.throwIfFailed(); } catch (int x) { didThrow = true; ASSERT_TRUE(x == -3); } catch (...) { FAIL() << "invalid exception type"; } ASSERT_TRUE(didThrow); /*EXPECT_EQ(-3, *obj.exception().get_exception()); EXPECT_EQ(-3, *std::move(obj).exception().get_exception()); EXPECT_EQ(-3, *as_const(obj).exception().get_exception()); EXPECT_EQ(-3, *std::move(as_const(obj)).exception().get_exception());*/ } }