1
0
Fork 0
arangodb/tests/Futures/Try-test.cpp

283 lines
7.7 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @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 <class T>
constexpr T const& as_const(T& t) noexcept {
return t;
}
template <class T>
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 <bool Nothrow>
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<A> t_a(std::move(a));
// Try<Unit> t_void;
ASSERT_TRUE(5 == t_a.get().x());
}
TEST(FuturesTryTest, in_place) {
Try<A> t_a(in_place, 5);
ASSERT_TRUE(5 == t_a.get().x());
}
TEST(FuturesTryTest, in_place_nested) {
Try<Try<A>> 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<ThrowingCopyConstructor> t1{in_place, counter};
Try<ThrowingCopyConstructor> 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<ThrowingCopyConstructor> t1{in_place, counter};
Try<ThrowingCopyConstructor> 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<A> 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<void> 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<MutableContainer> t{in_place};
auto val = MoveConstructOnly(std::move(t).get().val);
static_cast<void>(val);
}
{
const Try<MutableContainer> t{in_place};
auto val = (*(std::move(t))).val;
static_cast<void>(val);
}
}
// Make sure we can copy Trys for copyable types
TEST(FuturesTryTest, copy) {
Try<int> t;
auto t2 = t;
}
// But don't choke on move-only types
TEST(FuturesTryTest, moveOnly) {
Try<std::unique_ptr<int>> t;
std::vector<Try<std::unique_ptr<int>>> 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<int>();
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<ML, ActualML>::value));
ASSERT_TRUE((std::is_same<MR, ActualMR>::value));
ASSERT_TRUE((std::is_same<CL, ActualCL>::value));
ASSERT_TRUE((std::is_same<CR, ActualCR>::value));
}
{
auto obj = Try<int>(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<int>(std::make_exception_ptr<int>(-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<int>());
EXPECT_EQ(-3, *std::move(obj).exception().get_exception<int>());
EXPECT_EQ(-3, *as_const(obj).exception().get_exception<int>());
EXPECT_EQ(-3, *std::move(as_const(obj)).exception().get_exception<int>());*/
}
{
auto obj = Try<void>();
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<ML, ActualML>::value));
ASSERT_TRUE((std::is_same<MR, ActualMR>::value));
ASSERT_TRUE((std::is_same<CL, ActualCL>::value));
ASSERT_TRUE((std::is_same<CR, ActualCR>::value));
}
{
auto obj = Try<void>();
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<void>(std::make_exception_ptr<int>(-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<int>());
EXPECT_EQ(-3, *std::move(obj).exception().get_exception<int>());
EXPECT_EQ(-3, *as_const(obj).exception().get_exception<int>());
EXPECT_EQ(-3, *std::move(as_const(obj)).exception().get_exception<int>());*/
}
}