fix build when sanitizers are enabled and jemalloc is disabled
[folly.git] / folly / test / FunctionTest.cpp
index 7c5159c9ae0873f28ac1b8e5dd209fefbbb435a6..34217a2c825d028e318ed940cdfb5cfd61920dc2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 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 <array>
 #include <cstdarg>
 
 #include <folly/Function.h>
 
 #include <folly/Memory.h>
-#include <gtest/gtest.h>
+#include <folly/portability/GTest.h>
 
 using folly::Function;
 
@@ -49,8 +50,131 @@ struct Functor {
     return oldvalue;
   }
 };
+
+template <typename Ret, typename... Args>
+void deduceArgs(Function<Ret(Args...)>) {}
+
+struct CallableButNotCopyable {
+  CallableButNotCopyable() {}
+  CallableButNotCopyable(CallableButNotCopyable const&) = delete;
+  CallableButNotCopyable(CallableButNotCopyable&&) = delete;
+  CallableButNotCopyable& operator=(CallableButNotCopyable const&) = delete;
+  CallableButNotCopyable& operator=(CallableButNotCopyable&&) = delete;
+  template <class... Args>
+  void operator()(Args&&...) const {}
+};
+
 } // namespace
 
+// TEST =====================================================================
+// Test constructibility and non-constructibility for some tricky conversions
+static_assert(
+    !std::is_assignable<Function<void()>, CallableButNotCopyable>::value,
+    "");
+static_assert(
+    !std::is_constructible<Function<void()>, CallableButNotCopyable&>::value,
+    "");
+static_assert(
+    !std::is_constructible<Function<void() const>, CallableButNotCopyable>::
+        value,
+    "");
+static_assert(
+    !std::is_constructible<Function<void() const>, CallableButNotCopyable&>::
+        value,
+    "");
+
+static_assert(
+    !std::is_assignable<Function<void()>, CallableButNotCopyable>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<void()>, CallableButNotCopyable&>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<void() const>, CallableButNotCopyable>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<void() const>, CallableButNotCopyable&>::value,
+    "");
+
+static_assert(
+    std::is_constructible<Function<int(int)>, Function<int(int) const>>::value,
+    "");
+static_assert(
+    !std::is_constructible<Function<int(int) const>, Function<int(int)>>::value,
+    "");
+static_assert(
+    std::is_constructible<Function<int(short)>, Function<short(int) const>>::
+        value,
+    "");
+static_assert(
+    !std::is_constructible<Function<int(short) const>, Function<short(int)>>::
+        value,
+    "");
+static_assert(
+    !std::is_constructible<Function<int(int)>, Function<int(int) const>&>::
+        value,
+    "");
+static_assert(
+    !std::is_constructible<Function<int(int) const>, Function<int(int)>&>::
+        value,
+    "");
+static_assert(
+    !std::is_constructible<Function<int(short)>, Function<short(int) const>&>::
+        value,
+    "");
+static_assert(
+    !std::is_constructible<Function<int(short) const>, Function<short(int)>&>::
+        value,
+    "");
+
+static_assert(
+    std::is_assignable<Function<int(int)>, Function<int(int) const>>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<int(int) const>, Function<int(int)>>::value,
+    "");
+static_assert(
+    std::is_assignable<Function<int(short)>, Function<short(int) const>>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<int(short) const>, Function<short(int)>>::
+        value,
+    "");
+static_assert(
+    !std::is_assignable<Function<int(int)>, Function<int(int) const>&>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<int(int) const>, Function<int(int)>&>::value,
+    "");
+static_assert(
+    !std::is_assignable<Function<int(short)>, Function<short(int) const>&>::
+        value,
+    "");
+static_assert(
+    !std::is_assignable<Function<int(short) const>, Function<short(int)>&>::
+        value,
+    "");
+
+static_assert(
+    std::is_nothrow_constructible<
+        Function<int(int)>,
+        Function<int(int) const>>::value,
+    "");
+static_assert(
+    !std::is_nothrow_constructible<
+        Function<int(short)>,
+        Function<short(int) const>>::value,
+    "");
+static_assert(
+    std::is_nothrow_assignable<Function<int(int)>, Function<int(int) const>>::
+        value,
+    "");
+static_assert(
+    !std::is_nothrow_assignable<
+        Function<int(short)>,
+        Function<short(int) const>>::value,
+    "");
+
 // TEST =====================================================================
 // InvokeFunctor & InvokeReference
 
@@ -96,13 +220,15 @@ TEST(Function, Emptiness_T) {
   Function<int(int)> g([](int x) { return x + 1; });
   EXPECT_NE(g, nullptr);
   EXPECT_NE(nullptr, g);
-  EXPECT_TRUE(g);
+  // Explicitly convert to bool to work around
+  // https://github.com/google/googletest/issues/429
+  EXPECT_TRUE(bool(g));
   EXPECT_EQ(100, g(99));
 
   Function<int(int)> h(&func_int_int_add_25);
   EXPECT_NE(h, nullptr);
   EXPECT_NE(nullptr, h);
-  EXPECT_TRUE(h);
+  EXPECT_TRUE(bool(h));
   EXPECT_EQ(125, h(100));
 
   h = {};
@@ -187,14 +313,19 @@ TEST(Function, Bind) {
 // NonCopyableLambda
 
 TEST(Function, NonCopyableLambda) {
-  auto unique_ptr_int = folly::make_unique<int>(900);
+  auto unique_ptr_int = std::make_unique<int>(900);
   EXPECT_EQ(900, *unique_ptr_int);
 
-  char fooData[64] = {0};
-  EXPECT_EQ(0, fooData[0]); // suppress gcc warning about fooData not being used
+  struct {
+    char data[64];
+  } fooData = {{0}};
+  (void)fooData; // suppress gcc warning about fooData not being used
 
   auto functor = std::bind(
-      [fooData](std::unique_ptr<int>& up) mutable { return ++*up; },
+      [fooData](std::unique_ptr<int>& up) mutable {
+        (void)fooData;
+        return ++*up;
+      },
       std::move(unique_ptr_int));
 
   EXPECT_EQ(901, functor());
@@ -453,7 +584,7 @@ TEST(Function, CaptureCopyMoveCount) {
   Function<size_t(void)> uf1 = std::move(lambda1);
 
   // Max copies: 0. Max copy+moves: 2.
-  EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
+  EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 3);
   EXPECT_LE(cmt.copyCount(), 0);
 
   cmt.resetCounters();
@@ -465,7 +596,7 @@ TEST(Function, CaptureCopyMoveCount) {
   Function<size_t(void)> uf2 = lambda2;
 
   // Max copies: 1. Max copy+moves: 2.
-  EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 2);
+  EXPECT_LE(cmt.moveCount() + cmt.copyCount(), 3);
   EXPECT_LE(cmt.copyCount(), 1);
 
   // Invoking Function must not make copies/moves of the callable
@@ -813,6 +944,80 @@ TEST(Function, asStdFunction_args_const) {
   EXPECT_EQ(1, i);
 }
 
+// TEST =====================================================================
+// asSharedProxy_*
+
+TEST(Function, asSharedProxy_void) {
+  int i = 0;
+  folly::Function<void()> f = [&i] { ++i; };
+  auto sp = std::move(f).asSharedProxy();
+  auto spcopy = sp;
+  sp();
+  EXPECT_EQ(1, i);
+  spcopy();
+  EXPECT_EQ(2, i);
+}
+
+TEST(Function, asSharedProxy_void_const) {
+  int i = 0;
+  folly::Function<void() const> f = [&i] { ++i; };
+  auto sp = std::move(f).asSharedProxy();
+  auto spcopy = sp;
+  sp();
+  EXPECT_EQ(1, i);
+  spcopy();
+  EXPECT_EQ(2, i);
+}
+
+TEST(Function, asSharedProxy_return) {
+  folly::Function<int()> f = [i = 0]() mutable {
+    ++i;
+    return i;
+  };
+  auto sp = std::move(f).asSharedProxy();
+  auto spcopy = sp;
+  EXPECT_EQ(1, sp());
+  EXPECT_EQ(2, spcopy());
+}
+
+TEST(Function, asSharedProxy_return_const) {
+  int i = 0;
+  folly::Function<int() const> f = [&i] {
+    ++i;
+    return i;
+  };
+  auto sp = std::move(f).asSharedProxy();
+  auto spcopy = sp;
+  EXPECT_EQ(1, sp());
+  EXPECT_EQ(2, spcopy());
+}
+
+TEST(Function, asSharedProxy_args) {
+  int i = 0;
+  folly::Function<int(int, int)> f = [&](int x, int y) mutable {
+    ++i;
+    return x + y * 2;
+  };
+  auto sp = std::move(f).asSharedProxy();
+  auto spcopy = sp;
+  EXPECT_EQ(120, sp(100, 10));
+  EXPECT_EQ(1, i);
+  EXPECT_EQ(120, spcopy(100, 10));
+  EXPECT_EQ(2, i);
+}
+
+TEST(Function, asSharedProxy_args_const) {
+  int i = 0;
+  folly::Function<int(int, int) const> f = [&i](int x, int y) {
+    ++i;
+    return x * 100 + y * 10 + i;
+  };
+  auto sp = std::move(f).asSharedProxy();
+  auto spcopy = sp;
+  EXPECT_EQ(561, sp(5, 6));
+  EXPECT_EQ(562, spcopy(5, 6));
+}
+
 TEST(Function, NoAllocatedMemoryAfterMove) {
   Functor<int, 100> foo;
 
@@ -843,9 +1048,54 @@ TEST(Function, EmptyAfterConstCast) {
   EXPECT_FALSE(func2);
 }
 
-TEST(Function, SelfMoveAssign) {
-  Function<int()> f = [] { return 0; };
+TEST(Function, SelfStdSwap) {
+  Function<int()> f = [] { return 42; };
+  f.swap(f);
+  EXPECT_TRUE(bool(f));
+  EXPECT_EQ(42, f());
+  std::swap(f, f);
+  EXPECT_TRUE(bool(f));
+  EXPECT_EQ(42, f());
+  folly::swap(f, f);
+  EXPECT_TRUE(bool(f));
+  EXPECT_EQ(42, f());
+}
+
+TEST(Function, SelfMove) {
+  Function<int()> f = [] { return 42; };
   Function<int()>& g = f;
-  f = std::move(g);
-  EXPECT_TRUE(f);
+  f = std::move(g); // shouldn't crash!
+  (void)bool(f); // valid but unspecified state
+  f = [] { return 43; };
+  EXPECT_TRUE(bool(f));
+  EXPECT_EQ(43, f());
+}
+
+TEST(Function, DeducableArguments) {
+  deduceArgs(Function<void()>{[] {}});
+  deduceArgs(Function<void(int, float)>{[](int, float) {}});
+  deduceArgs(Function<int(int, float)>{[](int i, float) { return i; }});
+}
+
+TEST(Function, CtorWithCopy) {
+  struct X {
+    X() {}
+    X(X const&) noexcept(true) {}
+    X& operator=(X const&) = default;
+  };
+  struct Y {
+    Y() {}
+    Y(Y const&) noexcept(false) {}
+    Y(Y&&) noexcept(true) {}
+    Y& operator=(Y&&) = default;
+    Y& operator=(Y const&) = default;
+  };
+  auto lx = [x = X()]{};
+  auto ly = [y = Y()]{};
+  EXPECT_TRUE(noexcept(Function<void()>(lx)));
+  EXPECT_FALSE(noexcept(Function<void()>(ly)));
+}
+
+TEST(Function, Bug_T23346238) {
+  const Function<void()> nullfun;
 }