7d89fb164b
android::base::expected is an Android implementation of the std::expected proposal. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0323r7.html For usage, refer to the expected.h header file and expected_test.cpp Bug: 132145659 Test: libbase_test Change-Id: I65d3a1ecf8654d9858989755dfd0065c81f7b209
745 lines
20 KiB
C++
745 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "android-base/expected.h"
|
|
|
|
#include <cstdio>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
using android::base::expected;
|
|
using android::base::unexpected;
|
|
|
|
typedef expected<int, int> exp_int;
|
|
typedef expected<double, double> exp_double;
|
|
typedef expected<std::string, std::string> exp_string;
|
|
typedef expected<std::pair<std::string, int>, int> exp_pair;
|
|
|
|
struct T {
|
|
int a;
|
|
int b;
|
|
T() = default;
|
|
T(int a, int b) noexcept : a(a), b(b) {}
|
|
};
|
|
bool operator==(const T& x, const T& y) {
|
|
return x.a == y.a && x.b == y.b;
|
|
}
|
|
bool operator!=(const T& x, const T& y) {
|
|
return x.a != y.a || x.b != y.b;
|
|
}
|
|
|
|
struct E {
|
|
std::string message;
|
|
int cause;
|
|
E(const std::string& message, int cause) : message(message), cause(cause) {}
|
|
};
|
|
|
|
typedef expected<T,E> exp_complex;
|
|
|
|
TEST(Expected, testDefaultConstructible) {
|
|
exp_int e;
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_EQ(0, e.value());
|
|
|
|
exp_complex e2;
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(T(0,0), e2.value());
|
|
}
|
|
|
|
TEST(Expected, testCopyConstructible) {
|
|
exp_int e;
|
|
exp_int e2 = e;
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(0, e.value());
|
|
EXPECT_EQ(0, e2.value());
|
|
}
|
|
|
|
TEST(Expected, testMoveConstructible) {
|
|
exp_int e;
|
|
exp_int e2 = std::move(e);
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(0, e.value());
|
|
EXPECT_EQ(0, e2.value());
|
|
|
|
exp_string e3(std::string("hello"));
|
|
exp_string e4 = std::move(e3);
|
|
|
|
EXPECT_TRUE(e3.has_value());
|
|
EXPECT_TRUE(e4.has_value());
|
|
EXPECT_EQ("", e3.value()); // e3 is moved
|
|
EXPECT_EQ("hello", e4.value());
|
|
}
|
|
|
|
TEST(Expected, testCopyConstructibleFromConvertibleType) {
|
|
exp_double e = 3.3f;
|
|
exp_int e2 = e;
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(3.3f, e.value());
|
|
EXPECT_EQ(3, e2.value());
|
|
}
|
|
|
|
TEST(Expected, testMoveConstructibleFromConvertibleType) {
|
|
exp_double e = 3.3f;
|
|
exp_int e2 = std::move(e);
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(3.3f, e.value());
|
|
EXPECT_EQ(3, e2.value());
|
|
}
|
|
|
|
TEST(Expected, testConstructibleFromValue) {
|
|
exp_int e = 3;
|
|
exp_double e2 = 5.5f;
|
|
exp_string e3 = std::string("hello");
|
|
exp_complex e4 = T(10, 20);
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_TRUE(e3.has_value());
|
|
EXPECT_TRUE(e4.has_value());
|
|
EXPECT_EQ(3, e.value());
|
|
EXPECT_EQ(5.5f, e2.value());
|
|
EXPECT_EQ("hello", e3.value());
|
|
EXPECT_EQ(T(10,20), e4.value());
|
|
}
|
|
|
|
TEST(Expected, testConstructibleFromMovedValue) {
|
|
std::string hello = "hello";
|
|
exp_string e = std::move(hello);
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_EQ("hello", e.value());
|
|
EXPECT_EQ("", hello);
|
|
}
|
|
|
|
TEST(Expected, testConstructibleFromConvertibleValue) {
|
|
exp_int e = 3.3f; // double to int
|
|
exp_string e2 = "hello"; // char* to std::string
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_EQ(3, e.value());
|
|
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ("hello", e2.value());
|
|
}
|
|
|
|
TEST(Expected, testConstructibleFromUnexpected) {
|
|
exp_int::unexpected_type unexp = unexpected(10);
|
|
exp_int e = unexp;
|
|
|
|
exp_double::unexpected_type unexp2 = unexpected(10.5f);
|
|
exp_double e2 = unexp2;
|
|
|
|
exp_string::unexpected_type unexp3 = unexpected(std::string("error"));
|
|
exp_string e3 = unexp3;
|
|
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_FALSE(e2.has_value());
|
|
EXPECT_FALSE(e3.has_value());
|
|
EXPECT_EQ(10, e.error());
|
|
EXPECT_EQ(10.5f, e2.error());
|
|
EXPECT_EQ("error", e3.error());
|
|
}
|
|
|
|
TEST(Expected, testMoveConstructibleFromUnexpected) {
|
|
exp_int e = unexpected(10);
|
|
exp_double e2 = unexpected(10.5f);
|
|
exp_string e3 = unexpected(std::string("error"));
|
|
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_FALSE(e2.has_value());
|
|
EXPECT_FALSE(e3.has_value());
|
|
EXPECT_EQ(10, e.error());
|
|
EXPECT_EQ(10.5f, e2.error());
|
|
EXPECT_EQ("error", e3.error());
|
|
}
|
|
|
|
TEST(Expected, testConstructibleByForwarding) {
|
|
exp_string e(std::in_place, 5, 'a');
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_EQ("aaaaa", e.value());
|
|
|
|
exp_string e2({'a', 'b', 'c'});
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ("abc", e2.value());
|
|
|
|
exp_pair e3({"hello", 30});
|
|
EXPECT_TRUE(e3.has_value());
|
|
EXPECT_EQ("hello",e3->first);
|
|
EXPECT_EQ(30,e3->second);
|
|
}
|
|
|
|
TEST(Expected, testDestructible) {
|
|
bool destroyed = false;
|
|
struct T {
|
|
bool* flag_;
|
|
T(bool* flag) : flag_(flag) {}
|
|
~T() { *flag_ = true; }
|
|
};
|
|
{
|
|
expected<T, int> exp = T(&destroyed);
|
|
}
|
|
EXPECT_TRUE(destroyed);
|
|
}
|
|
|
|
TEST(Expected, testAssignable) {
|
|
exp_int e = 10;
|
|
exp_int e2 = 20;
|
|
e = e2;
|
|
|
|
EXPECT_EQ(20, e.value());
|
|
EXPECT_EQ(20, e2.value());
|
|
|
|
exp_int e3 = 10;
|
|
exp_int e4 = 20;
|
|
e3 = std::move(e4);
|
|
|
|
EXPECT_EQ(20, e3.value());
|
|
EXPECT_EQ(20, e4.value());
|
|
}
|
|
|
|
TEST(Expected, testAssignableFromValue) {
|
|
exp_int e = 10;
|
|
e = 20;
|
|
EXPECT_EQ(20, e.value());
|
|
|
|
exp_double e2 = 3.5f;
|
|
e2 = 10.5f;
|
|
EXPECT_EQ(10.5f, e2.value());
|
|
|
|
exp_string e3 = "hello";
|
|
e3 = "world";
|
|
EXPECT_EQ("world", e3.value());
|
|
}
|
|
|
|
TEST(Expected, testAssignableFromUnexpected) {
|
|
exp_int e = 10;
|
|
e = unexpected(30);
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_EQ(30, e.error());
|
|
|
|
exp_double e2 = 3.5f;
|
|
e2 = unexpected(10.5f);
|
|
EXPECT_FALSE(e2.has_value());
|
|
EXPECT_EQ(10.5f, e2.error());
|
|
|
|
exp_string e3 = "hello";
|
|
e3 = unexpected("world");
|
|
EXPECT_FALSE(e3.has_value());
|
|
EXPECT_EQ("world", e3.error());
|
|
}
|
|
|
|
TEST(Expected, testAssignableFromMovedValue) {
|
|
std::string world = "world";
|
|
exp_string e = "hello";
|
|
e = std::move(world);
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_EQ("world", e.value());
|
|
EXPECT_EQ("", world);
|
|
}
|
|
|
|
TEST(Expected, testAssignableFromMovedUnexpected) {
|
|
std::string world = "world";
|
|
exp_string e = "hello";
|
|
e = unexpected(std::move(world));
|
|
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_EQ("world", e.error());
|
|
EXPECT_EQ("", world);
|
|
}
|
|
|
|
TEST(Expected, testEmplace) {
|
|
struct T {
|
|
int a;
|
|
double b;
|
|
T() {}
|
|
T(int a, double b) noexcept : a(a), b(b) {}
|
|
};
|
|
expected<T, int> exp;
|
|
T& t = exp.emplace(3, 10.5f);
|
|
|
|
EXPECT_TRUE(exp.has_value());
|
|
EXPECT_EQ(3, t.a);
|
|
EXPECT_EQ(10.5f, t.b);
|
|
EXPECT_EQ(3, exp.value().a);
|
|
EXPECT_EQ(10.5, exp.value().b);
|
|
}
|
|
|
|
TEST(Expected, testSwapExpectedExpected) {
|
|
exp_int e = 10;
|
|
exp_int e2 = 20;
|
|
e.swap(e2);
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(20, e.value());
|
|
EXPECT_EQ(10, e2.value());
|
|
}
|
|
|
|
TEST(Expected, testSwapUnexpectedUnexpected) {
|
|
exp_int e = unexpected(10);
|
|
exp_int e2 = unexpected(20);
|
|
e.swap(e2);
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_FALSE(e2.has_value());
|
|
EXPECT_EQ(20, e.error());
|
|
EXPECT_EQ(10, e2.error());
|
|
}
|
|
|
|
TEST(Expected, testSwapExpectedUnepected) {
|
|
exp_int e = 10;
|
|
exp_int e2 = unexpected(30);
|
|
e.swap(e2);
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(30, e.error());
|
|
EXPECT_EQ(10, e2.value());
|
|
}
|
|
|
|
TEST(Expected, testDereference) {
|
|
struct T {
|
|
int a;
|
|
double b;
|
|
T() {}
|
|
T(int a, double b) : a(a), b(b) {}
|
|
};
|
|
expected<T, int> exp = T(3, 10.5f);
|
|
|
|
EXPECT_EQ(3, exp->a);
|
|
EXPECT_EQ(10.5f, exp->b);
|
|
|
|
EXPECT_EQ(3, (*exp).a);
|
|
EXPECT_EQ(10.5f, (*exp).b);
|
|
}
|
|
|
|
TEST(Expected, testTest) {
|
|
exp_int e = 10;
|
|
EXPECT_TRUE(e);
|
|
EXPECT_TRUE(e.has_value());
|
|
|
|
exp_int e2 = unexpected(10);
|
|
EXPECT_FALSE(e2);
|
|
EXPECT_FALSE(e2.has_value());
|
|
}
|
|
|
|
TEST(Expected, testGetValue) {
|
|
exp_int e = 10;
|
|
EXPECT_EQ(10, e.value());
|
|
EXPECT_EQ(10, e.value_or(20));
|
|
|
|
exp_int e2 = unexpected(10);
|
|
EXPECT_EQ(10, e2.error());
|
|
EXPECT_EQ(20, e2.value_or(20));
|
|
}
|
|
|
|
TEST(Expected, testSameValues) {
|
|
exp_int e = 10;
|
|
exp_int e2 = 10;
|
|
EXPECT_TRUE(e == e2);
|
|
EXPECT_TRUE(e2 == e);
|
|
EXPECT_FALSE(e != e2);
|
|
EXPECT_FALSE(e2 != e);
|
|
}
|
|
|
|
TEST(Expected, testDifferentValues) {
|
|
exp_int e = 10;
|
|
exp_int e2 = 20;
|
|
EXPECT_FALSE(e == e2);
|
|
EXPECT_FALSE(e2 == e);
|
|
EXPECT_TRUE(e != e2);
|
|
EXPECT_TRUE(e2 != e);
|
|
}
|
|
|
|
TEST(Expected, testValueWithError) {
|
|
exp_int e = 10;
|
|
exp_int e2 = unexpected(10);
|
|
EXPECT_FALSE(e == e2);
|
|
EXPECT_FALSE(e2 == e);
|
|
EXPECT_TRUE(e != e2);
|
|
EXPECT_TRUE(e2 != e);
|
|
}
|
|
|
|
TEST(Expected, testSameErrors) {
|
|
exp_int e = unexpected(10);
|
|
exp_int e2 = unexpected(10);
|
|
EXPECT_TRUE(e == e2);
|
|
EXPECT_TRUE(e2 == e);
|
|
EXPECT_FALSE(e != e2);
|
|
EXPECT_FALSE(e2 != e);
|
|
}
|
|
|
|
TEST(Expected, testDifferentErrors) {
|
|
exp_int e = unexpected(10);
|
|
exp_int e2 = unexpected(20);
|
|
EXPECT_FALSE(e == e2);
|
|
EXPECT_FALSE(e2 == e);
|
|
EXPECT_TRUE(e != e2);
|
|
EXPECT_TRUE(e2 != e);
|
|
}
|
|
|
|
TEST(Expected, testCompareWithSameValue) {
|
|
exp_int e = 10;
|
|
int value = 10;
|
|
EXPECT_TRUE(e == value);
|
|
EXPECT_TRUE(value == e);
|
|
EXPECT_FALSE(e != value);
|
|
EXPECT_FALSE(value != e);
|
|
}
|
|
|
|
TEST(Expected, testCompareWithDifferentValue) {
|
|
exp_int e = 10;
|
|
int value = 20;
|
|
EXPECT_FALSE(e == value);
|
|
EXPECT_FALSE(value == e);
|
|
EXPECT_TRUE(e != value);
|
|
EXPECT_TRUE(value != e);
|
|
}
|
|
|
|
TEST(Expected, testCompareWithSameError) {
|
|
exp_int e = unexpected(10);
|
|
exp_int::unexpected_type error = 10;
|
|
EXPECT_TRUE(e == error);
|
|
EXPECT_TRUE(error == e);
|
|
EXPECT_FALSE(e != error);
|
|
EXPECT_FALSE(error != e);
|
|
}
|
|
|
|
TEST(Expected, testCompareWithDifferentError) {
|
|
exp_int e = unexpected(10);
|
|
exp_int::unexpected_type error = 20;
|
|
EXPECT_FALSE(e == error);
|
|
EXPECT_FALSE(error == e);
|
|
EXPECT_TRUE(e != error);
|
|
EXPECT_TRUE(error != e);
|
|
}
|
|
|
|
TEST(Expected, testDivideExample) {
|
|
struct QR {
|
|
int quotient;
|
|
int remainder;
|
|
QR(int q, int r) noexcept : quotient(q), remainder(r) {}
|
|
bool operator==(const QR& rhs) const {
|
|
return quotient == rhs.quotient && remainder == rhs.remainder;
|
|
}
|
|
bool operator!=(const QR& rhs) const {
|
|
return quotient != rhs.quotient || remainder == rhs.remainder;
|
|
}
|
|
};
|
|
|
|
auto divide = [](int x, int y) -> expected<QR,E> {
|
|
if (y == 0) {
|
|
return unexpected(E("divide by zero", -1));
|
|
} else {
|
|
return QR(x / y, x % y);
|
|
}
|
|
};
|
|
|
|
EXPECT_FALSE(divide(10, 0));
|
|
EXPECT_EQ("divide by zero", divide(10, 0).error().message);
|
|
EXPECT_EQ(-1, divide(10, 0).error().cause);
|
|
|
|
EXPECT_TRUE(divide(10, 3));
|
|
EXPECT_EQ(QR(3, 1), divide(10, 3));
|
|
}
|
|
|
|
TEST(Expected, testPair) {
|
|
auto test = [](bool yes) -> exp_pair {
|
|
if (yes) {
|
|
return exp_pair({"yes", 42});
|
|
} else {
|
|
return unexpected(42);
|
|
}
|
|
};
|
|
|
|
auto r = test(true);
|
|
EXPECT_TRUE(r);
|
|
EXPECT_EQ("yes", r->first);
|
|
}
|
|
|
|
// copied from result_test.cpp
|
|
struct ConstructorTracker {
|
|
static size_t constructor_called;
|
|
static size_t copy_constructor_called;
|
|
static size_t move_constructor_called;
|
|
static size_t copy_assignment_called;
|
|
static size_t move_assignment_called;
|
|
|
|
template <typename T,
|
|
typename std::enable_if_t<std::is_convertible_v<T, std::string>>* = nullptr>
|
|
ConstructorTracker(T&& string) : string(string) {
|
|
++constructor_called;
|
|
}
|
|
ConstructorTracker(const ConstructorTracker& ct) {
|
|
++copy_constructor_called;
|
|
string = ct.string;
|
|
}
|
|
ConstructorTracker(ConstructorTracker&& ct) noexcept {
|
|
++move_constructor_called;
|
|
string = std::move(ct.string);
|
|
}
|
|
ConstructorTracker& operator=(const ConstructorTracker& ct) {
|
|
++copy_assignment_called;
|
|
string = ct.string;
|
|
return *this;
|
|
}
|
|
ConstructorTracker& operator=(ConstructorTracker&& ct) noexcept {
|
|
++move_assignment_called;
|
|
string = std::move(ct.string);
|
|
return *this;
|
|
}
|
|
static void Reset() {
|
|
constructor_called = 0;
|
|
copy_constructor_called = 0;
|
|
move_constructor_called = 0;
|
|
copy_assignment_called = 0;
|
|
move_assignment_called = 0;
|
|
}
|
|
std::string string;
|
|
};
|
|
|
|
size_t ConstructorTracker::constructor_called = 0;
|
|
size_t ConstructorTracker::copy_constructor_called = 0;
|
|
size_t ConstructorTracker::move_constructor_called = 0;
|
|
size_t ConstructorTracker::copy_assignment_called = 0;
|
|
size_t ConstructorTracker::move_assignment_called = 0;
|
|
|
|
typedef expected<ConstructorTracker, int> exp_track;
|
|
|
|
TEST(Expected, testNumberOfCopies) {
|
|
// default constructor
|
|
ConstructorTracker::Reset();
|
|
exp_track e("hello");
|
|
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
// copy constructor
|
|
ConstructorTracker::Reset();
|
|
exp_track e2 = e;
|
|
EXPECT_EQ(0U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
// move constructor
|
|
ConstructorTracker::Reset();
|
|
exp_track e3 = std::move(e);
|
|
EXPECT_EQ(0U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
// construct from lvalue
|
|
ConstructorTracker::Reset();
|
|
ConstructorTracker ct = "hello";
|
|
exp_track e4(ct);
|
|
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
// construct from rvalue
|
|
ConstructorTracker::Reset();
|
|
ConstructorTracker ct2 = "hello";
|
|
exp_track e5(std::move(ct2));
|
|
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
// copy assignment
|
|
ConstructorTracker::Reset();
|
|
exp_track e6 = "hello";
|
|
exp_track e7 = "world";
|
|
e7 = e6;
|
|
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
// move assignment
|
|
ConstructorTracker::Reset();
|
|
exp_track e8 = "hello";
|
|
exp_track e9 = "world";
|
|
e9 = std::move(e8);
|
|
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::move_assignment_called);
|
|
|
|
// swap
|
|
ConstructorTracker::Reset();
|
|
exp_track e10 = "hello";
|
|
exp_track e11 = "world";
|
|
std::swap(e10, e11);
|
|
EXPECT_EQ(2U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(2U, ConstructorTracker::move_assignment_called);
|
|
}
|
|
|
|
TEST(Expected, testNoCopyOnReturn) {
|
|
auto test = [](const std::string& in) -> exp_track {
|
|
if (in.empty()) {
|
|
return "literal string";
|
|
}
|
|
if (in == "test2") {
|
|
return ConstructorTracker(in + in + "2");
|
|
}
|
|
ConstructorTracker result(in + " " + in);
|
|
return result;
|
|
};
|
|
|
|
ConstructorTracker::Reset();
|
|
auto result1 = test("");
|
|
ASSERT_TRUE(result1);
|
|
EXPECT_EQ("literal string", result1->string);
|
|
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
ConstructorTracker::Reset();
|
|
auto result2 = test("test2");
|
|
ASSERT_TRUE(result2);
|
|
EXPECT_EQ("test2test22", result2->string);
|
|
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
|
|
ConstructorTracker::Reset();
|
|
auto result3 = test("test3");
|
|
ASSERT_TRUE(result3);
|
|
EXPECT_EQ("test3 test3", result3->string);
|
|
EXPECT_EQ(1U, ConstructorTracker::constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_constructor_called);
|
|
EXPECT_EQ(1U, ConstructorTracker::move_constructor_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::copy_assignment_called);
|
|
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
|
|
}
|
|
|
|
TEST(Expected, testNested) {
|
|
expected<exp_string, std::string> e = "hello";
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e.value().has_value());
|
|
EXPECT_TRUE(e);
|
|
EXPECT_TRUE(*e);
|
|
EXPECT_EQ("hello", e.value().value());
|
|
|
|
expected<exp_string, std::string> e2 = unexpected("world");
|
|
EXPECT_FALSE(e2.has_value());
|
|
EXPECT_FALSE(e2);
|
|
EXPECT_EQ("world", e2.error());
|
|
|
|
expected<exp_string, std::string> e3 = exp_string(unexpected("world"));
|
|
EXPECT_TRUE(e3.has_value());
|
|
EXPECT_FALSE(e3.value().has_value());
|
|
EXPECT_TRUE(e3);
|
|
EXPECT_FALSE(*e3);
|
|
EXPECT_EQ("world", e3.value().error());
|
|
}
|
|
|
|
constexpr bool equals(const char* a, const char* b) {
|
|
return (a == nullptr && b == nullptr) ||
|
|
(a != nullptr && b != nullptr && *a == *b &&
|
|
(*a == '\0' || equals(a + 1, b + 1)));
|
|
}
|
|
|
|
TEST(Expected, testConstexpr) {
|
|
// Compliation error will occur if these expressions can't be
|
|
// evaluated at compile time
|
|
constexpr exp_int e(3);
|
|
constexpr exp_int::unexpected_type err(3);
|
|
constexpr int i = 4;
|
|
|
|
// default constructor
|
|
static_assert(exp_int().value() == 0);
|
|
// copy constructor
|
|
static_assert(exp_int(e).value() == 3);
|
|
// move constructor
|
|
static_assert(exp_int(exp_int(4)).value() == 4);
|
|
// copy construct from value
|
|
static_assert(exp_int(i).value() == 4);
|
|
// copy construct from unexpected
|
|
static_assert(exp_int(err).error() == 3);
|
|
// move costruct from unexpected
|
|
static_assert(exp_int(unexpected(3)).error() == 3);
|
|
// observers
|
|
static_assert(*exp_int(3) == 3);
|
|
static_assert(exp_int(3).has_value() == true);
|
|
static_assert(exp_int(3).value_or(4) == 3);
|
|
|
|
typedef expected<const char*, int> exp_s;
|
|
constexpr exp_s s("hello");
|
|
constexpr const char* c = "hello";
|
|
static_assert(equals(exp_s().value(), nullptr));
|
|
static_assert(equals(exp_s(s).value(), "hello"));
|
|
static_assert(equals(exp_s(exp_s("hello")).value(), "hello"));
|
|
static_assert(equals(exp_s("hello").value(), "hello"));
|
|
static_assert(equals(exp_s(c).value(), "hello"));
|
|
}
|
|
|
|
TEST(Expected, testWithNonConstructible) {
|
|
struct AssertNotConstructed {
|
|
AssertNotConstructed() = delete;
|
|
};
|
|
|
|
expected<int, AssertNotConstructed> v(42);
|
|
EXPECT_TRUE(v.has_value());
|
|
EXPECT_EQ(42, v.value());
|
|
|
|
expected<AssertNotConstructed, int> e(unexpected(42));
|
|
EXPECT_FALSE(e.has_value());
|
|
EXPECT_EQ(42, e.error());
|
|
}
|
|
|
|
TEST(Expected, testWithMoveOnlyType) {
|
|
typedef expected<std::unique_ptr<int>,std::unique_ptr<int>> exp_ptr;
|
|
exp_ptr e(std::make_unique<int>(3));
|
|
exp_ptr e2(unexpected(std::make_unique<int>(4)));
|
|
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_FALSE(e2.has_value());
|
|
EXPECT_EQ(3, *(e.value()));
|
|
EXPECT_EQ(4, *(e2.error()));
|
|
|
|
e2 = std::move(e);
|
|
EXPECT_TRUE(e.has_value());
|
|
EXPECT_TRUE(e2.has_value());
|
|
EXPECT_EQ(3, *(e2.value()));
|
|
}
|