blob: 8703be4adf6cac8a21c7b9c5fbc449d283c9e394 [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"
Niels Möller84255bb2017-10-06 13:43:23 +020017#include "rtc_base/refcountedobject.h"
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020018
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000019namespace rtc {
20
21namespace {
22
Magnus Jedvertb2745472015-08-25 17:56:22 +020023struct LifeTimeCheck;
24
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000025struct MethodBindTester {
26 void NullaryVoid() { ++call_count; }
27 int NullaryInt() { ++call_count; return 1; }
28 int NullaryConst() const { ++call_count; return 2; }
29 void UnaryVoid(int dummy) { ++call_count; }
30 template <class T> T Identity(T value) { ++call_count; return value; }
noahric5d9b92b2015-10-24 11:14:46 -070031 int UnaryByPointer(int* value) const {
32 ++call_count;
33 return ++(*value);
34 }
35 int UnaryByRef(const int& value) const {
36 ++call_count;
37 return ++const_cast<int&>(value);
38 }
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000039 int Multiply(int a, int b) const { ++call_count; return a * b; }
Magnus Jedvertb2745472015-08-25 17:56:22 +020040 void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
41 EXPECT_TRUE(object.get() != nullptr);
42 }
43
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000044 mutable int call_count;
45};
46
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020047struct A { int dummy; };
48struct B: public RefCountInterface { int dummy; };
49struct C: public A, B {};
50struct D {
51 int AddRef();
52};
53struct E: public D {
54 int Release();
55};
56struct F {
57 void AddRef();
58 void Release();
59};
60
Magnus Jedvertb2745472015-08-25 17:56:22 +020061struct LifeTimeCheck {
62 LifeTimeCheck() : ref_count_(0) {}
63 void AddRef() { ++ref_count_; }
64 void Release() { --ref_count_; }
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020065 void NullaryVoid() {}
Magnus Jedvertb2745472015-08-25 17:56:22 +020066 int ref_count_;
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020067};
68
henrike@webrtc.orgf0488722014-05-13 18:00:26 +000069int Return42() { return 42; }
70int Negate(int a) { return -a; }
71int Multiply(int a, int b) { return a * b; }
72
73} // namespace
74
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020075// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
76// compile time.
77#define EXPECT_IS_CAPTURED_AS_PTR(T) \
78 static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
79 "PointerType")
80#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T) \
81 static_assert( \
82 is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
83 "PointerType")
84
85EXPECT_IS_CAPTURED_AS_PTR(void);
86EXPECT_IS_CAPTURED_AS_PTR(int);
87EXPECT_IS_CAPTURED_AS_PTR(double);
88EXPECT_IS_CAPTURED_AS_PTR(A);
89EXPECT_IS_CAPTURED_AS_PTR(D);
90EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
deadbeefccaaffb2017-02-25 12:56:20 -080091EXPECT_IS_CAPTURED_AS_PTR(
92 decltype(Unretained<RefCountedObject<RefCountInterface>>));
Magnus Jedvertd3de9c52015-08-20 16:03:52 +020093
94EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
95EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
96EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
97EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
98EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
99EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
100EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
101EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
Magnus Jedvert1b40a9a2015-10-12 15:50:43 +0200102EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200103
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000104TEST(BindTest, BindToMethod) {
105 MethodBindTester object = {0};
106 EXPECT_EQ(0, object.call_count);
107 Bind(&MethodBindTester::NullaryVoid, &object)();
108 EXPECT_EQ(1, object.call_count);
109 EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
110 EXPECT_EQ(2, object.call_count);
111 EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
112 static_cast<const MethodBindTester*>(&object))());
113 EXPECT_EQ(3, object.call_count);
114 Bind(&MethodBindTester::UnaryVoid, &object, 5)();
115 EXPECT_EQ(4, object.call_count);
116 EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
117 EXPECT_EQ(5, object.call_count);
118 const std::string string_value("test string");
119 EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
120 &object, string_value)());
121 EXPECT_EQ(6, object.call_count);
122 int value = 11;
noahric5d9b92b2015-10-24 11:14:46 -0700123 // Bind binds by value, even if the method signature is by reference, so
124 // "reference" binds require pointers.
125 EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000126 EXPECT_EQ(12, value);
127 EXPECT_EQ(7, object.call_count);
noahric5d9b92b2015-10-24 11:14:46 -0700128 // It's possible to bind to a function that takes a const reference, though
129 // the capture will be a copy. See UnaryByRef hackery above where it removes
130 // the const to make sure the underlying storage is, in fact, a copy.
131 EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
132 // But the original value is unmodified.
133 EXPECT_EQ(12, value);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000134 EXPECT_EQ(8, object.call_count);
noahric5d9b92b2015-10-24 11:14:46 -0700135 EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
136 EXPECT_EQ(9, object.call_count);
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000137}
138
139TEST(BindTest, BindToFunction) {
140 EXPECT_EQ(42, Bind(&Return42)());
141 EXPECT_EQ(3, Bind(&Negate, -3)());
142 EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
143}
144
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200145// Test Bind where method object implements RefCountInterface and is passed as a
146// pointer.
147TEST(BindTest, CapturePointerAsScopedRefPtr) {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200148 LifeTimeCheck object;
149 EXPECT_EQ(object.ref_count_, 0);
150 scoped_refptr<LifeTimeCheck> scoped_object(&object);
151 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200152 {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200153 auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
154 EXPECT_EQ(object.ref_count_, 2);
155 scoped_object = nullptr;
156 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200157 }
Magnus Jedvertb2745472015-08-25 17:56:22 +0200158 EXPECT_EQ(object.ref_count_, 0);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200159}
160
161// Test Bind where method object implements RefCountInterface and is passed as a
162// scoped_refptr<>.
163TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200164 LifeTimeCheck object;
165 EXPECT_EQ(object.ref_count_, 0);
166 scoped_refptr<LifeTimeCheck> scoped_object(&object);
167 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200168 {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200169 auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
170 EXPECT_EQ(object.ref_count_, 2);
171 scoped_object = nullptr;
172 EXPECT_EQ(object.ref_count_, 1);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200173 }
Magnus Jedvertb2745472015-08-25 17:56:22 +0200174 EXPECT_EQ(object.ref_count_, 0);
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200175}
176
177// Test Bind where method object is captured as scoped_refptr<> and the functor
178// dies while there are references left.
179TEST(BindTest, FunctorReleasesObjectOnDestruction) {
Magnus Jedvertb2745472015-08-25 17:56:22 +0200180 LifeTimeCheck object;
181 EXPECT_EQ(object.ref_count_, 0);
182 scoped_refptr<LifeTimeCheck> scoped_object(&object);
183 EXPECT_EQ(object.ref_count_, 1);
184 Bind(&LifeTimeCheck::NullaryVoid, &object)();
185 EXPECT_EQ(object.ref_count_, 1);
186 scoped_object = nullptr;
187 EXPECT_EQ(object.ref_count_, 0);
188}
189
190// Test Bind with scoped_refptr<> argument.
191TEST(BindTest, ScopedRefPointerArgument) {
192 LifeTimeCheck object;
193 EXPECT_EQ(object.ref_count_, 0);
194 scoped_refptr<LifeTimeCheck> scoped_object(&object);
195 EXPECT_EQ(object.ref_count_, 1);
196 {
197 MethodBindTester bind_tester;
198 auto functor =
199 Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
200 EXPECT_EQ(object.ref_count_, 2);
201 }
202 EXPECT_EQ(object.ref_count_, 1);
203 scoped_object = nullptr;
204 EXPECT_EQ(object.ref_count_, 0);
205}
206
207namespace {
208
209const int* Ref(const int& a) { return &a; }
210
211} // anonymous namespace
212
noahric5d9b92b2015-10-24 11:14:46 -0700213// Test Bind with non-scoped_refptr<> reference argument, which should be
214// modified to a non-reference capture.
Magnus Jedvertb2745472015-08-25 17:56:22 +0200215TEST(BindTest, RefArgument) {
216 const int x = 42;
noahric5d9b92b2015-10-24 11:14:46 -0700217 EXPECT_EQ(&x, Ref(x));
218 // Bind() should make a copy of |x|, i.e. the pointers should be different.
Magnus Jedvertb2745472015-08-25 17:56:22 +0200219 auto functor = Bind(&Ref, x);
noahric5d9b92b2015-10-24 11:14:46 -0700220 EXPECT_NE(&x, functor());
Magnus Jedvertd3de9c52015-08-20 16:03:52 +0200221}
222
henrike@webrtc.orgf0488722014-05-13 18:00:26 +0000223} // namespace rtc