blob: 7028ced35189e248b94515ebcac59ff72fd46121 [file] [log] [blame]
henrike@webrtc.orgf0488722014-05-13 18:00:26 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
deadbeef08187d42017-02-25 11:21:18 -080011#include <type_traits>
12
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/bind.h"
14#include "rtc_base/gunit.h"
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000015
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/refcount.h"
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020017
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000018namespace rtc {
19
20namespace {
21
Magnus Jedvertb2745472015-08-25 17:56:22 +020022struct LifeTimeCheck;
23
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000024struct MethodBindTester {
25 void NullaryVoid() { ++call_count; }
26 int NullaryInt() { ++call_count; return 1; }
27 int NullaryConst() const { ++call_count; return 2; }
28 void UnaryVoid(int dummy) { ++call_count; }
29 template <class T> T Identity(T value) { ++call_count; return value; }
noahric5d9b92b2015-10-24 11:14:46 -070030 int UnaryByPointer(int* value) const {
31 ++call_count;
32 return ++(*value);
33 }
34 int UnaryByRef(const int& value) const {
35 ++call_count;
36 return ++const_cast<int&>(value);
37 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000038 int Multiply(int a, int b) const { ++call_count; return a * b; }
Magnus Jedvertb2745472015-08-25 17:56:22 +020039 void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
40 EXPECT_TRUE(object.get() != nullptr);
41 }
42
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000043 mutable int call_count;
44};
45
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020046struct A { int dummy; };
47struct B: public RefCountInterface { int dummy; };
48struct C: public A, B {};
49struct D {
50 int AddRef();
51};
52struct E: public D {
53 int Release();
54};
55struct F {
56 void AddRef();
57 void Release();
58};
59
Magnus Jedvertb2745472015-08-25 17:56:22 +020060struct LifeTimeCheck {
61 LifeTimeCheck() : ref_count_(0) {}
62 void AddRef() { ++ref_count_; }
63 void Release() { --ref_count_; }
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020064 void NullaryVoid() {}
Magnus Jedvertb2745472015-08-25 17:56:22 +020065 int ref_count_;
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020066};
67
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000068int Return42() { return 42; }
69int Negate(int a) { return -a; }
70int Multiply(int a, int b) { return a * b; }
71
72} // namespace
73
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020074// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
75// compile time.
76#define EXPECT_IS_CAPTURED_AS_PTR(T) \
77 static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
78 "PointerType")
79#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T) \
80 static_assert( \
81 is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
82 "PointerType")
83
84EXPECT_IS_CAPTURED_AS_PTR(void);
85EXPECT_IS_CAPTURED_AS_PTR(int);
86EXPECT_IS_CAPTURED_AS_PTR(double);
87EXPECT_IS_CAPTURED_AS_PTR(A);
88EXPECT_IS_CAPTURED_AS_PTR(D);
89EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
deadbeefccaaffb2017-02-25 12:56:20 -080090EXPECT_IS_CAPTURED_AS_PTR(
91 decltype(Unretained<RefCountedObject<RefCountInterface>>));
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020092
93EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
94EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
95EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
96EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
97EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
98EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
99EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
100EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
Magnus Jedvert1b40a9a2015-10-12 15:50:43 +0200101EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200102
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000103TEST(BindTest, BindToMethod) {
104 MethodBindTester object = {0};
105 EXPECT_EQ(0, object.call_count);
106 Bind(&MethodBindTester::NullaryVoid, &object)();
107 EXPECT_EQ(1, object.call_count);
108 EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
109 EXPECT_EQ(2, object.call_count);
110 EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
111 static_cast<const MethodBindTester*>(&object))());
112 EXPECT_EQ(3, object.call_count);
113 Bind(&MethodBindTester::UnaryVoid, &object, 5)();
114 EXPECT_EQ(4, object.call_count);
115 EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
116 EXPECT_EQ(5, object.call_count);
117 const std::string string_value("test string");
118 EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
119 &object, string_value)());
120 EXPECT_EQ(6, object.call_count);
121 int value = 11;
noahric5d9b92b2015-10-24 11:14:46 -0700122 // Bind binds by value, even if the method signature is by reference, so
123 // "reference" binds require pointers.
124 EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000125 EXPECT_EQ(12, value);
126 EXPECT_EQ(7, object.call_count);
noahric5d9b92b2015-10-24 11:14:46 -0700127 // It's possible to bind to a function that takes a const reference, though
128 // the capture will be a copy. See UnaryByRef hackery above where it removes
129 // the const to make sure the underlying storage is, in fact, a copy.
130 EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
131 // But the original value is unmodified.
132 EXPECT_EQ(12, value);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000133 EXPECT_EQ(8, object.call_count);
noahric5d9b92b2015-10-24 11:14:46 -0700134 EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
135 EXPECT_EQ(9, object.call_count);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000136}
137
138TEST(BindTest, BindToFunction) {
139 EXPECT_EQ(42, Bind(&Return42)());
140 EXPECT_EQ(3, Bind(&Negate, -3)());
141 EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
142}
143
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200144// Test Bind where method object implements RefCountInterface and is passed as a
145// pointer.
146TEST(BindTest, CapturePointerAsScopedRefPtr) {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200147 LifeTimeCheck object;
148 EXPECT_EQ(object.ref_count_, 0);
149 scoped_refptr<LifeTimeCheck> scoped_object(&object);
150 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200151 {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200152 auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
153 EXPECT_EQ(object.ref_count_, 2);
154 scoped_object = nullptr;
155 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200156 }
Magnus Jedvertb2745472015-08-25 17:56:22 +0200157 EXPECT_EQ(object.ref_count_, 0);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200158}
159
160// Test Bind where method object implements RefCountInterface and is passed as a
161// scoped_refptr<>.
162TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200163 LifeTimeCheck object;
164 EXPECT_EQ(object.ref_count_, 0);
165 scoped_refptr<LifeTimeCheck> scoped_object(&object);
166 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200167 {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200168 auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
169 EXPECT_EQ(object.ref_count_, 2);
170 scoped_object = nullptr;
171 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200172 }
Magnus Jedvertb2745472015-08-25 17:56:22 +0200173 EXPECT_EQ(object.ref_count_, 0);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200174}
175
176// Test Bind where method object is captured as scoped_refptr<> and the functor
177// dies while there are references left.
178TEST(BindTest, FunctorReleasesObjectOnDestruction) {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200179 LifeTimeCheck object;
180 EXPECT_EQ(object.ref_count_, 0);
181 scoped_refptr<LifeTimeCheck> scoped_object(&object);
182 EXPECT_EQ(object.ref_count_, 1);
183 Bind(&LifeTimeCheck::NullaryVoid, &object)();
184 EXPECT_EQ(object.ref_count_, 1);
185 scoped_object = nullptr;
186 EXPECT_EQ(object.ref_count_, 0);
187}
188
189// Test Bind with scoped_refptr<> argument.
190TEST(BindTest, ScopedRefPointerArgument) {
191 LifeTimeCheck object;
192 EXPECT_EQ(object.ref_count_, 0);
193 scoped_refptr<LifeTimeCheck> scoped_object(&object);
194 EXPECT_EQ(object.ref_count_, 1);
195 {
196 MethodBindTester bind_tester;
197 auto functor =
198 Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
199 EXPECT_EQ(object.ref_count_, 2);
200 }
201 EXPECT_EQ(object.ref_count_, 1);
202 scoped_object = nullptr;
203 EXPECT_EQ(object.ref_count_, 0);
204}
205
206namespace {
207
208const int* Ref(const int& a) { return &a; }
209
210} // anonymous namespace
211
noahric5d9b92b2015-10-24 11:14:46 -0700212// Test Bind with non-scoped_refptr<> reference argument, which should be
213// modified to a non-reference capture.
Magnus Jedvertb2745472015-08-25 17:56:22 +0200214TEST(BindTest, RefArgument) {
215 const int x = 42;
noahric5d9b92b2015-10-24 11:14:46 -0700216 EXPECT_EQ(&x, Ref(x));
217 // Bind() should make a copy of |x|, i.e. the pointers should be different.
Magnus Jedvertb2745472015-08-25 17:56:22 +0200218 auto functor = Bind(&Ref, x);
noahric5d9b92b2015-10-24 11:14:46 -0700219 EXPECT_NE(&x, functor());
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200220}
221
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000222} // namespace rtc