non-throwing, non-allocating exception_wrapper
[folly.git] / folly / test / OptionalTest.cpp
index 1268ff662d74c9ca9444bfd9fc25f2d540bdf893..b7484dfa978b6488050eab02470ad15982ea9f67 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include "folly/Optional.h"
+#include <folly/Optional.h>
+#include <folly/portability/GTest.h>
 
-#include <memory>
-#include <vector>
 #include <algorithm>
 #include <iomanip>
+#include <memory>
 #include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <vector>
 
-#include <glog/logging.h>
-#include <gtest/gtest.h>
 #include <boost/optional.hpp>
 
-using namespace folly;
 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;
@@ -47,7 +59,7 @@ TEST(Optional, NoDefault) {
   Optional<NoDefault> x;
   EXPECT_FALSE(x);
   x.emplace(4, 5);
-  EXPECT_TRUE(x);
+  EXPECT_TRUE(bool(x));
   x.clear();
   EXPECT_FALSE(x);
 }
@@ -56,53 +68,140 @@ TEST(Optional, String) {
   Optional<std::string> maybeString;
   EXPECT_FALSE(maybeString);
   maybeString = "hello";
-  EXPECT_TRUE(maybeString);
+  EXPECT_TRUE(bool(maybeString));
 }
 
 TEST(Optional, Const) {
   { // default construct
     Optional<const int> opt;
-    EXPECT_FALSE(opt);
+    EXPECT_FALSE(bool(opt));
     opt.emplace(4);
-    EXPECT_EQ(opt, 4);
+    EXPECT_EQ(*opt, 4);
     opt.emplace(5);
-    EXPECT_EQ(opt, 5);
+    EXPECT_EQ(*opt, 5);
     opt.clear();
-    EXPECT_FALSE(opt);
+    EXPECT_FALSE(bool(opt));
   }
   { // copy-constructed
     const int x = 6;
     Optional<const int> opt(x);
-    EXPECT_EQ(opt, 6);
+    EXPECT_EQ(*opt, 6);
   }
   { // move-constructed
     const int x = 7;
     Optional<const int> opt(std::move(x));
-    EXPECT_EQ(opt, 7);
+    EXPECT_EQ(*opt, 7);
   }
   // no assignment allowed
 }
 
 TEST(Optional, Simple) {
   Optional<int> opt;
-  EXPECT_FALSE(opt);
+  EXPECT_FALSE(bool(opt));
+  EXPECT_EQ(42, opt.value_or(42));
   opt = 4;
-  EXPECT_TRUE(opt);
+  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(opt);
+  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&& other) noexcept {
+    s_ = std::move(other.s_);
+    other.s_ = "";
+    return *this;
+  }
+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_move) {
+  auto ptr = Optional<std::unique_ptr<int, ExpectingDeleter>>(
+      {new int(42), ExpectingDeleter{1337}}).value();
+  *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(opt);
+  EXPECT_FALSE(bool(opt));
   // empty->emplaced
   opt.emplace(new int(5));
-  EXPECT_TRUE(opt);
+  EXPECT_TRUE(bool(opt));
   EXPECT_EQ(5, **opt);
 
   opt.clear();
@@ -115,24 +214,24 @@ TEST(Optional, Unique) {
 
   // move it out by move construct
   Optional<unique_ptr<int>> moved(std::move(opt));
-  EXPECT_TRUE(moved);
-  EXPECT_FALSE(opt);
+  EXPECT_TRUE(bool(moved));
+  EXPECT_FALSE(bool(opt));
   EXPECT_EQ(7, **moved);
 
-  EXPECT_TRUE(moved);
+  EXPECT_TRUE(bool(moved));
   opt = std::move(moved); // move it back by move assign
-  EXPECT_FALSE(moved);
-  EXPECT_TRUE(opt);
+  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(opt);
+  EXPECT_FALSE(bool(opt));
   // empty->emplaced
   opt.emplace(new int(5));
-  EXPECT_TRUE(opt);
+  EXPECT_TRUE(bool(opt));
   ptr = opt.value();
   EXPECT_EQ(ptr.get(), opt->get());
   EXPECT_EQ(2, ptr.use_count());
@@ -176,7 +275,7 @@ TEST(Optional, Order) {
     { 3 },
   };
   std::sort(vect.begin(), vect.end());
-  EXPECT_TRUE(vect == expected);
+  EXPECT_EQ(vect, expected);
 }
 
 TEST(Optional, Swap) {
@@ -209,21 +308,16 @@ TEST(Optional, Comparisons) {
   Optional<int> o1(1);
   Optional<int> o2(2);
 
-  EXPECT_TRUE(o_ < 1);
-  EXPECT_TRUE(o_ <= 1);
-  EXPECT_TRUE(o_ <= o_);
-  EXPECT_TRUE(o_ == o_);
-  EXPECT_TRUE(o_ != 1);
-  EXPECT_TRUE(o_ >= o_);
-  EXPECT_TRUE(1 >= o_);
-  EXPECT_TRUE(1 > o_);
+  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 <= (o1));
+  EXPECT_TRUE(o1 == (o1));
   EXPECT_TRUE(o1 != o2);
-  EXPECT_TRUE(o1 >= o1);
+  EXPECT_TRUE(o1 >= (o1));
   EXPECT_TRUE(o2 >= o1);
   EXPECT_TRUE(o2 > o1);
 
@@ -231,11 +325,12 @@ TEST(Optional, Comparisons) {
   EXPECT_FALSE(o2 <= o1);
   EXPECT_FALSE(o2 <= o1);
   EXPECT_FALSE(o2 == o1);
-  EXPECT_FALSE(o1 != 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);
@@ -253,6 +348,69 @@ TEST(Optional, Comparisons) {
   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);
+  EXPECT_FALSE(b);
+
+  // 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) {
@@ -261,7 +419,137 @@ TEST(Optional, Pointee) {
   x = 1;
   EXPECT_TRUE(get_pointer(x));
   *get_pointer(x) = 2;
-  EXPECT_TRUE(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);
+}
+
+#if __clang__
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wself-move"
+#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);
+}
+
+#if __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);
+}
+
+TEST(Optional, NoThrowDefaultConstructible) {
+  EXPECT_TRUE(std::is_nothrow_default_constructible<Optional<bool>>::value);
+}
+
+struct NoDestructor {};
+
+struct WithDestructor {
+  ~WithDestructor();
+};
+
+TEST(Optional, TriviallyDestructible) {
+  // These could all be static_asserts but EXPECT_* give much nicer output on
+  // failure.
+  EXPECT_TRUE(std::is_trivially_destructible<Optional<NoDestructor>>::value);
+  EXPECT_TRUE(std::is_trivially_destructible<Optional<int>>::value);
+  EXPECT_FALSE(std::is_trivially_destructible<Optional<WithDestructor>>::value);
+}
+
+TEST(Optional, Hash) {
+  // Test it's usable in std::unordered map (compile time check)
+  std::unordered_map<Optional<int>, Optional<int>> obj;
+  // Also check the std::hash template can be instantiated by the compiler
+  std::hash<Optional<int>>()(none);
+  std::hash<Optional<int>>()(3);
+}
+}