blob: 974c8f422f6fa03cdc1c981919e4e440473df254 [file] [log] [blame]
/*
* Copyright 2015 Facebook, Inc.
*
* 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 <folly/Optional.h>
#include <memory>
#include <vector>
#include <algorithm>
#include <iomanip>
#include <string>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <boost/optional.hpp>
using std::unique_ptr;
using std::shared_ptr;
namespace folly {
template<class V>
std::ostream& operator<<(std::ostream& os, const Optional<V>& v) {
if (v) {
os << "Optional(" << v.value() << ')';
} else {
os << "None";
}
return os;
}
struct NoDefault {
NoDefault(int, int) {}
char a, b, c;
};
static_assert(sizeof(Optional<char>) == 2, "");
static_assert(sizeof(Optional<int>) == 8, "");
static_assert(sizeof(Optional<NoDefault>) == 4, "");
static_assert(sizeof(Optional<char>) == sizeof(boost::optional<char>), "");
static_assert(sizeof(Optional<short>) == sizeof(boost::optional<short>), "");
static_assert(sizeof(Optional<int>) == sizeof(boost::optional<int>), "");
static_assert(sizeof(Optional<double>) == sizeof(boost::optional<double>), "");
TEST(Optional, NoDefault) {
Optional<NoDefault> x;
EXPECT_FALSE(x);
x.emplace(4, 5);
EXPECT_TRUE(bool(x));
x.clear();
EXPECT_FALSE(x);
}
TEST(Optional, String) {
Optional<std::string> maybeString;
EXPECT_FALSE(maybeString);
maybeString = "hello";
EXPECT_TRUE(bool(maybeString));
}
TEST(Optional, Const) {
{ // default construct
Optional<const int> opt;
EXPECT_FALSE(bool(opt));
opt.emplace(4);
EXPECT_EQ(*opt, 4);
opt.emplace(5);
EXPECT_EQ(*opt, 5);
opt.clear();
EXPECT_FALSE(bool(opt));
}
{ // copy-constructed
const int x = 6;
Optional<const int> opt(x);
EXPECT_EQ(*opt, 6);
}
{ // move-constructed
const int x = 7;
Optional<const int> opt(std::move(x));
EXPECT_EQ(*opt, 7);
}
// no assignment allowed
}
TEST(Optional, Simple) {
Optional<int> opt;
EXPECT_FALSE(bool(opt));
EXPECT_EQ(42, opt.value_or(42));
opt = 4;
EXPECT_TRUE(bool(opt));
EXPECT_EQ(4, *opt);
EXPECT_EQ(4, opt.value_or(42));
opt = 5;
EXPECT_EQ(5, *opt);
opt.clear();
EXPECT_FALSE(bool(opt));
}
class MoveTester {
public:
/* implicit */ MoveTester(const char* s) : s_(s) {}
MoveTester(const MoveTester&) = default;
MoveTester(MoveTester&& other) noexcept {
s_ = std::move(other.s_);
other.s_ = "";
}
MoveTester& operator=(const MoveTester&) = default;
MoveTester& operator=(MoveTester&&) = default;
private:
friend bool operator==(const MoveTester& o1, const MoveTester& o2);
std::string s_;
};
bool operator==(const MoveTester& o1, const MoveTester& o2) {
return o1.s_ == o2.s_;
}
TEST(Optional, value_or_rvalue_arg) {
Optional<MoveTester> opt;
MoveTester dflt = "hello";
EXPECT_EQ("hello", opt.value_or(dflt));
EXPECT_EQ("hello", dflt);
EXPECT_EQ("hello", opt.value_or(std::move(dflt)));
EXPECT_EQ("", dflt);
EXPECT_EQ("world", opt.value_or("world"));
dflt = "hello";
// Make sure that the const overload works on const objects
const auto& optc = opt;
EXPECT_EQ("hello", optc.value_or(dflt));
EXPECT_EQ("hello", dflt);
EXPECT_EQ("hello", optc.value_or(std::move(dflt)));
EXPECT_EQ("", dflt);
EXPECT_EQ("world", optc.value_or("world"));
dflt = "hello";
opt = "meow";
EXPECT_EQ("meow", opt.value_or(dflt));
EXPECT_EQ("hello", dflt);
EXPECT_EQ("meow", opt.value_or(std::move(dflt)));
EXPECT_EQ("hello", dflt); // only moved if used
}
TEST(Optional, value_or_noncopyable) {
Optional<std::unique_ptr<int>> opt;
std::unique_ptr<int> dflt(new int(42));
EXPECT_EQ(42, *std::move(opt).value_or(std::move(dflt)));
}
struct ExpectingDeleter {
explicit ExpectingDeleter(int expected) : expected(expected) { }
int expected;
void operator()(const int* ptr) {
EXPECT_EQ(*ptr, expected);
delete ptr;
}
};
TEST(Optional, value_life_extention) {
// Extends the life of the value.
const auto& ptr = Optional<std::unique_ptr<int, ExpectingDeleter>>(
{new int(42), ExpectingDeleter{1337}}).value();
*ptr = 1337;
}
TEST(Optional, value_move) {
auto ptr = Optional<std::unique_ptr<int, ExpectingDeleter>>(
{new int(42), ExpectingDeleter{1337}}).value();
*ptr = 1337;
}
TEST(Optional, dereference_life_extention) {
// Extends the life of the value.
const auto& ptr = *Optional<std::unique_ptr<int, ExpectingDeleter>>(
{new int(42), ExpectingDeleter{1337}});
*ptr = 1337;
}
TEST(Optional, dereference_move) {
auto ptr = *Optional<std::unique_ptr<int, ExpectingDeleter>>(
{new int(42), ExpectingDeleter{1337}});
*ptr = 1337;
}
TEST(Optional, EmptyConstruct) {
Optional<int> opt;
EXPECT_FALSE(bool(opt));
Optional<int> test1(opt);
EXPECT_FALSE(bool(test1));
Optional<int> test2(std::move(opt));
EXPECT_FALSE(bool(test2));
}
TEST(Optional, Unique) {
Optional<unique_ptr<int>> opt;
opt.clear();
EXPECT_FALSE(bool(opt));
// empty->emplaced
opt.emplace(new int(5));
EXPECT_TRUE(bool(opt));
EXPECT_EQ(5, **opt);
opt.clear();
// empty->moved
opt = unique_ptr<int>(new int(6));
EXPECT_EQ(6, **opt);
// full->moved
opt = unique_ptr<int>(new int(7));
EXPECT_EQ(7, **opt);
// move it out by move construct
Optional<unique_ptr<int>> moved(std::move(opt));
EXPECT_TRUE(bool(moved));
EXPECT_FALSE(bool(opt));
EXPECT_EQ(7, **moved);
EXPECT_TRUE(bool(moved));
opt = std::move(moved); // move it back by move assign
EXPECT_FALSE(bool(moved));
EXPECT_TRUE(bool(opt));
EXPECT_EQ(7, **opt);
}
TEST(Optional, Shared) {
shared_ptr<int> ptr;
Optional<shared_ptr<int>> opt;
EXPECT_FALSE(bool(opt));
// empty->emplaced
opt.emplace(new int(5));
EXPECT_TRUE(bool(opt));
ptr = opt.value();
EXPECT_EQ(ptr.get(), opt->get());
EXPECT_EQ(2, ptr.use_count());
opt.clear();
EXPECT_EQ(1, ptr.use_count());
// full->copied
opt = ptr;
EXPECT_EQ(2, ptr.use_count());
EXPECT_EQ(ptr.get(), opt->get());
opt.clear();
EXPECT_EQ(1, ptr.use_count());
// full->moved
opt = std::move(ptr);
EXPECT_EQ(1, opt->use_count());
EXPECT_EQ(nullptr, ptr.get());
{
Optional<shared_ptr<int>> copied(opt);
EXPECT_EQ(2, opt->use_count());
Optional<shared_ptr<int>> moved(std::move(opt));
EXPECT_EQ(2, moved->use_count());
moved.emplace(new int(6));
EXPECT_EQ(1, moved->use_count());
copied = moved;
EXPECT_EQ(2, moved->use_count());
}
}
TEST(Optional, Order) {
std::vector<Optional<int>> vect{
{ none },
{ 3 },
{ 1 },
{ none },
{ 2 },
};
std::vector<Optional<int>> expected {
{ none },
{ none },
{ 1 },
{ 2 },
{ 3 },
};
std::sort(vect.begin(), vect.end());
EXPECT_EQ(vect, expected);
}
TEST(Optional, Swap) {
Optional<std::string> a;
Optional<std::string> b;
swap(a, b);
EXPECT_FALSE(a.hasValue());
EXPECT_FALSE(b.hasValue());
a = "hello";
EXPECT_TRUE(a.hasValue());
EXPECT_FALSE(b.hasValue());
EXPECT_EQ("hello", a.value());
swap(a, b);
EXPECT_FALSE(a.hasValue());
EXPECT_TRUE(b.hasValue());
EXPECT_EQ("hello", b.value());
a = "bye";
EXPECT_TRUE(a.hasValue());
EXPECT_EQ("bye", a.value());
swap(a, b);
}
TEST(Optional, Comparisons) {
Optional<int> o_;
Optional<int> o1(1);
Optional<int> o2(2);
EXPECT_TRUE(o_ <= (o_));
EXPECT_TRUE(o_ == (o_));
EXPECT_TRUE(o_ >= (o_));
EXPECT_TRUE(o1 < o2);
EXPECT_TRUE(o1 <= o2);
EXPECT_TRUE(o1 <= (o1));
EXPECT_TRUE(o1 == (o1));
EXPECT_TRUE(o1 != o2);
EXPECT_TRUE(o1 >= (o1));
EXPECT_TRUE(o2 >= o1);
EXPECT_TRUE(o2 > o1);
EXPECT_FALSE(o2 < o1);
EXPECT_FALSE(o2 <= o1);
EXPECT_FALSE(o2 <= o1);
EXPECT_FALSE(o2 == o1);
EXPECT_FALSE(o1 != (o1));
EXPECT_FALSE(o1 >= o2);
EXPECT_FALSE(o1 >= o2);
EXPECT_FALSE(o1 > o2);
/* folly::Optional explicitly doesn't support comparisons with contained value
EXPECT_TRUE(1 < o2);
EXPECT_TRUE(1 <= o2);
EXPECT_TRUE(1 <= o1);
EXPECT_TRUE(1 == o1);
EXPECT_TRUE(2 != o1);
EXPECT_TRUE(1 >= o1);
EXPECT_TRUE(2 >= o1);
EXPECT_TRUE(2 > o1);
EXPECT_FALSE(o2 < 1);
EXPECT_FALSE(o2 <= 1);
EXPECT_FALSE(o2 <= 1);
EXPECT_FALSE(o2 == 1);
EXPECT_FALSE(o2 != 2);
EXPECT_FALSE(o1 >= 2);
EXPECT_FALSE(o1 >= 2);
EXPECT_FALSE(o1 > 2);
*/
// boost::optional does support comparison with contained value, which can
// lead to confusion when a bool is contained
boost::optional<int> boi(3);
EXPECT_TRUE(boi < 5);
EXPECT_TRUE(boi <= 4);
EXPECT_TRUE(boi == 3);
EXPECT_TRUE(boi != 2);
EXPECT_TRUE(boi >= 1);
EXPECT_TRUE(boi > 0);
EXPECT_TRUE(1 < boi);
EXPECT_TRUE(2 <= boi);
EXPECT_TRUE(3 == boi);
EXPECT_TRUE(4 != boi);
EXPECT_TRUE(5 >= boi);
EXPECT_TRUE(6 > boi);
boost::optional<bool> bob(false);
EXPECT_TRUE((bool)bob);
EXPECT_TRUE(bob == false); // well that was confusing
EXPECT_FALSE(bob != false);
}
TEST(Optional, Conversions) {
Optional<bool> mbool;
Optional<short> mshort;
Optional<char*> mstr;
Optional<int> mint;
//These don't compile
//bool b = mbool;
//short s = mshort;
//char* c = mstr;
//int x = mint;
//char* c(mstr);
//short s(mshort);
//int x(mint);
// intended explicit operator bool, for if (opt).
bool b(mbool);
// Truthy tests work and are not ambiguous
if (mbool && mshort && mstr && mint) { // only checks not-empty
if (*mbool && *mshort && *mstr && *mint) { // only checks value
;
}
}
mbool = false;
EXPECT_TRUE(bool(mbool));
EXPECT_FALSE(*mbool);
mbool = true;
EXPECT_TRUE(bool(mbool));
EXPECT_TRUE(*mbool);
mbool = none;
EXPECT_FALSE(bool(mbool));
// No conversion allowed; does not compile
// EXPECT_TRUE(mbool == false);
}
TEST(Optional, Pointee) {
Optional<int> x;
EXPECT_FALSE(get_pointer(x));
x = 1;
EXPECT_TRUE(get_pointer(x));
*get_pointer(x) = 2;
EXPECT_TRUE(*x == 2);
x = none;
EXPECT_FALSE(get_pointer(x));
}
TEST(Optional, MakeOptional) {
// const L-value version
const std::string s("abc");
auto optStr = make_optional(s);
ASSERT_TRUE(optStr.hasValue());
EXPECT_EQ(*optStr, "abc");
*optStr = "cde";
EXPECT_EQ(s, "abc");
EXPECT_EQ(*optStr, "cde");
// L-value version
std::string s2("abc");
auto optStr2 = make_optional(s2);
ASSERT_TRUE(optStr2.hasValue());
EXPECT_EQ(*optStr2, "abc");
*optStr2 = "cde";
// it's vital to check that s2 wasn't clobbered
EXPECT_EQ(s2, "abc");
// L-value reference version
std::string& s3(s2);
auto optStr3 = make_optional(s3);
ASSERT_TRUE(optStr3.hasValue());
EXPECT_EQ(*optStr3, "abc");
*optStr3 = "cde";
EXPECT_EQ(s3, "abc");
// R-value ref version
unique_ptr<int> pInt(new int(3));
auto optIntPtr = make_optional(std::move(pInt));
EXPECT_TRUE(pInt.get() == nullptr);
ASSERT_TRUE(optIntPtr.hasValue());
EXPECT_EQ(**optIntPtr, 3);
}
#ifdef __clang__
# pragma clang diagnostic push
# if __clang_major__ > 3 || __clang_minor__ >= 6
# pragma clang diagnostic ignored "-Wself-move"
# endif
#endif
TEST(Optional, SelfAssignment) {
Optional<int> a = 42;
a = a;
ASSERT_TRUE(a.hasValue() && a.value() == 42);
Optional<int> b = 23333333;
b = std::move(b);
ASSERT_TRUE(b.hasValue() && b.value() == 23333333);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
class ContainsOptional {
public:
ContainsOptional() { }
explicit ContainsOptional(int x) : opt_(x) { }
bool hasValue() const { return opt_.hasValue(); }
int value() const { return opt_.value(); }
ContainsOptional(const ContainsOptional &other) = default;
ContainsOptional& operator=(const ContainsOptional &other) = default;
ContainsOptional(ContainsOptional &&other) = default;
ContainsOptional& operator=(ContainsOptional &&other) = default;
private:
Optional<int> opt_;
};
/**
* Test that a class containing an Optional can be copy and move assigned.
* This was broken under gcc 4.7 until assignment operators were explicitly
* defined.
*/
TEST(Optional, AssignmentContained) {
{
ContainsOptional source(5), target;
target = source;
EXPECT_TRUE(target.hasValue());
EXPECT_EQ(5, target.value());
}
{
ContainsOptional source(5), target;
target = std::move(source);
EXPECT_TRUE(target.hasValue());
EXPECT_EQ(5, target.value());
EXPECT_FALSE(source.hasValue());
}
{
ContainsOptional opt_uninit, target(10);
target = opt_uninit;
EXPECT_FALSE(target.hasValue());
}
}
TEST(Optional, Exceptions) {
Optional<int> empty;
EXPECT_THROW(empty.value(), OptionalEmptyException);
}
}