webrtc/rtc_base/bind_unittest.cc
Niels Möller 84255bbe3b Add explicit includes of refcountedobject.h where it is used.
This is in preparation for deleting the include in rtc_base/refcount.h,
but that change has to wait for some downstream applications to 
not rely in the indirect include.

Partial reland of "Make rtc_base/refcount.h self contained, not including refcountedobject.h."

This is a reland of b7239a9dc8
Original change's description:
> Make rtc_base/refcount.h self contained, not including refcountedobject.h.
> 
> The refcount.h file doesn't depend on anything from
> refcountedobject.h. The motivation of this change to make it possible
> to add additional declarations to refcount.h, and include it from
> refcountedobject.h.
> 
> Bug: webrtc:8270
> Change-Id: I24f6131f471e675570968d00065ff9b1f55e3373
> Reviewed-on: https://webrtc-review.googlesource.com/5760
> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
> Commit-Queue: Niels Moller <nisse@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#20106}

Bug: webrtc:8270
Change-Id: I63a42712f6c1ec83823c629d1a954fd1a04d4a6c
Reviewed-on: https://webrtc-review.googlesource.com/7281
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20185}
2017-10-06 13:00:14 +00:00

223 lines
7.2 KiB
C++

/*
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <type_traits>
#include "rtc_base/bind.h"
#include "rtc_base/gunit.h"
#include "rtc_base/refcount.h"
#include "rtc_base/refcountedobject.h"
namespace rtc {
namespace {
struct LifeTimeCheck;
struct MethodBindTester {
void NullaryVoid() { ++call_count; }
int NullaryInt() { ++call_count; return 1; }
int NullaryConst() const { ++call_count; return 2; }
void UnaryVoid(int dummy) { ++call_count; }
template <class T> T Identity(T value) { ++call_count; return value; }
int UnaryByPointer(int* value) const {
++call_count;
return ++(*value);
}
int UnaryByRef(const int& value) const {
++call_count;
return ++const_cast<int&>(value);
}
int Multiply(int a, int b) const { ++call_count; return a * b; }
void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
EXPECT_TRUE(object.get() != nullptr);
}
mutable int call_count;
};
struct A { int dummy; };
struct B: public RefCountInterface { int dummy; };
struct C: public A, B {};
struct D {
int AddRef();
};
struct E: public D {
int Release();
};
struct F {
void AddRef();
void Release();
};
struct LifeTimeCheck {
LifeTimeCheck() : ref_count_(0) {}
void AddRef() { ++ref_count_; }
void Release() { --ref_count_; }
void NullaryVoid() {}
int ref_count_;
};
int Return42() { return 42; }
int Negate(int a) { return -a; }
int Multiply(int a, int b) { return a * b; }
} // namespace
// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
// compile time.
#define EXPECT_IS_CAPTURED_AS_PTR(T) \
static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
"PointerType")
#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T) \
static_assert( \
is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
"PointerType")
EXPECT_IS_CAPTURED_AS_PTR(void);
EXPECT_IS_CAPTURED_AS_PTR(int);
EXPECT_IS_CAPTURED_AS_PTR(double);
EXPECT_IS_CAPTURED_AS_PTR(A);
EXPECT_IS_CAPTURED_AS_PTR(D);
EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
EXPECT_IS_CAPTURED_AS_PTR(
decltype(Unretained<RefCountedObject<RefCountInterface>>));
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
TEST(BindTest, BindToMethod) {
MethodBindTester object = {0};
EXPECT_EQ(0, object.call_count);
Bind(&MethodBindTester::NullaryVoid, &object)();
EXPECT_EQ(1, object.call_count);
EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
EXPECT_EQ(2, object.call_count);
EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
static_cast<const MethodBindTester*>(&object))());
EXPECT_EQ(3, object.call_count);
Bind(&MethodBindTester::UnaryVoid, &object, 5)();
EXPECT_EQ(4, object.call_count);
EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
EXPECT_EQ(5, object.call_count);
const std::string string_value("test string");
EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
&object, string_value)());
EXPECT_EQ(6, object.call_count);
int value = 11;
// Bind binds by value, even if the method signature is by reference, so
// "reference" binds require pointers.
EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
EXPECT_EQ(12, value);
EXPECT_EQ(7, object.call_count);
// It's possible to bind to a function that takes a const reference, though
// the capture will be a copy. See UnaryByRef hackery above where it removes
// the const to make sure the underlying storage is, in fact, a copy.
EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
// But the original value is unmodified.
EXPECT_EQ(12, value);
EXPECT_EQ(8, object.call_count);
EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
EXPECT_EQ(9, object.call_count);
}
TEST(BindTest, BindToFunction) {
EXPECT_EQ(42, Bind(&Return42)());
EXPECT_EQ(3, Bind(&Negate, -3)());
EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
}
// Test Bind where method object implements RefCountInterface and is passed as a
// pointer.
TEST(BindTest, CapturePointerAsScopedRefPtr) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
{
auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
EXPECT_EQ(object.ref_count_, 2);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 1);
}
EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind where method object implements RefCountInterface and is passed as a
// scoped_refptr<>.
TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
{
auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
EXPECT_EQ(object.ref_count_, 2);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 1);
}
EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind where method object is captured as scoped_refptr<> and the functor
// dies while there are references left.
TEST(BindTest, FunctorReleasesObjectOnDestruction) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
Bind(&LifeTimeCheck::NullaryVoid, &object)();
EXPECT_EQ(object.ref_count_, 1);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 0);
}
// Test Bind with scoped_refptr<> argument.
TEST(BindTest, ScopedRefPointerArgument) {
LifeTimeCheck object;
EXPECT_EQ(object.ref_count_, 0);
scoped_refptr<LifeTimeCheck> scoped_object(&object);
EXPECT_EQ(object.ref_count_, 1);
{
MethodBindTester bind_tester;
auto functor =
Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
EXPECT_EQ(object.ref_count_, 2);
}
EXPECT_EQ(object.ref_count_, 1);
scoped_object = nullptr;
EXPECT_EQ(object.ref_count_, 0);
}
namespace {
const int* Ref(const int& a) { return &a; }
} // anonymous namespace
// Test Bind with non-scoped_refptr<> reference argument, which should be
// modified to a non-reference capture.
TEST(BindTest, RefArgument) {
const int x = 42;
EXPECT_EQ(&x, Ref(x));
// Bind() should make a copy of |x|, i.e. the pointers should be different.
auto functor = Bind(&Ref, x);
EXPECT_NE(&x, functor());
}
} // namespace rtc