1/*
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
11#include "webrtc/base/bind.h"
12#include "webrtc/base/gunit.h"
13
14#include "webrtc/base/refcount.h"
15
16namespace rtc {
17
18namespace {
19
20struct LifeTimeCheck;
21
22struct MethodBindTester {
23  void NullaryVoid() { ++call_count; }
24  int NullaryInt() { ++call_count; return 1; }
25  int NullaryConst() const { ++call_count; return 2; }
26  void UnaryVoid(int dummy) { ++call_count; }
27  template <class T> T Identity(T value) { ++call_count; return value; }
28  int UnaryByPointer(int* value) const {
29    ++call_count;
30    return ++(*value);
31  }
32  int UnaryByRef(const int& value) const {
33    ++call_count;
34    return ++const_cast<int&>(value);
35  }
36  int Multiply(int a, int b) const { ++call_count; return a * b; }
37  void RefArgument(const scoped_refptr<LifeTimeCheck>& object) {
38    EXPECT_TRUE(object.get() != nullptr);
39  }
40
41  mutable int call_count;
42};
43
44struct A { int dummy; };
45struct B: public RefCountInterface { int dummy; };
46struct C: public A, B {};
47struct D {
48  int AddRef();
49};
50struct E: public D {
51  int Release();
52};
53struct F {
54  void AddRef();
55  void Release();
56};
57
58struct LifeTimeCheck {
59  LifeTimeCheck() : ref_count_(0) {}
60  void AddRef() { ++ref_count_; }
61  void Release() { --ref_count_; }
62  void NullaryVoid() {}
63  int ref_count_;
64};
65
66int Return42() { return 42; }
67int Negate(int a) { return -a; }
68int Multiply(int a, int b) { return a * b; }
69
70}  // namespace
71
72// Try to catch any problem with scoped_refptr type deduction in rtc::Bind at
73// compile time.
74static_assert(
75    is_same<
76        rtc::remove_reference<const scoped_refptr<RefCountInterface>&>::type,
77        const scoped_refptr<RefCountInterface>>::value,
78    "const scoped_refptr& should be captured by value");
79
80static_assert(is_same<rtc::remove_reference<const scoped_refptr<F>&>::type,
81                      const scoped_refptr<F>>::value,
82              "const scoped_refptr& should be captured by value");
83
84static_assert(
85    is_same<rtc::remove_reference<const int&>::type, const int>::value,
86    "const int& should be captured as const int");
87
88static_assert(is_same<rtc::remove_reference<const F&>::type, const F>::value,
89              "const F& should be captured as const F");
90
91static_assert(is_same<rtc::remove_reference<F&>::type, F>::value,
92              "F& should be captured as F");
93
94#define EXPECT_IS_CAPTURED_AS_PTR(T)                              \
95  static_assert(is_same<detail::PointerType<T>::type, T*>::value, \
96                "PointerType")
97#define EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(T)                        \
98  static_assert(                                                      \
99      is_same<detail::PointerType<T>::type, scoped_refptr<T>>::value, \
100      "PointerType")
101
102EXPECT_IS_CAPTURED_AS_PTR(void);
103EXPECT_IS_CAPTURED_AS_PTR(int);
104EXPECT_IS_CAPTURED_AS_PTR(double);
105EXPECT_IS_CAPTURED_AS_PTR(A);
106EXPECT_IS_CAPTURED_AS_PTR(D);
107EXPECT_IS_CAPTURED_AS_PTR(RefCountInterface*);
108
109EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountInterface);
110EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(B);
111EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(C);
112EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(E);
113EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(F);
114EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<RefCountInterface>);
115EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<B>);
116EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(RefCountedObject<C>);
117EXPECT_IS_CAPTURED_AS_SCOPED_REFPTR(const RefCountedObject<RefCountInterface>);
118
119TEST(BindTest, BindToMethod) {
120  MethodBindTester object = {0};
121  EXPECT_EQ(0, object.call_count);
122  Bind(&MethodBindTester::NullaryVoid, &object)();
123  EXPECT_EQ(1, object.call_count);
124  EXPECT_EQ(1, Bind(&MethodBindTester::NullaryInt, &object)());
125  EXPECT_EQ(2, object.call_count);
126  EXPECT_EQ(2, Bind(&MethodBindTester::NullaryConst,
127                    static_cast<const MethodBindTester*>(&object))());
128  EXPECT_EQ(3, object.call_count);
129  Bind(&MethodBindTester::UnaryVoid, &object, 5)();
130  EXPECT_EQ(4, object.call_count);
131  EXPECT_EQ(100, Bind(&MethodBindTester::Identity<int>, &object, 100)());
132  EXPECT_EQ(5, object.call_count);
133  const std::string string_value("test string");
134  EXPECT_EQ(string_value, Bind(&MethodBindTester::Identity<std::string>,
135                               &object, string_value)());
136  EXPECT_EQ(6, object.call_count);
137  int value = 11;
138  // Bind binds by value, even if the method signature is by reference, so
139  // "reference" binds require pointers.
140  EXPECT_EQ(12, Bind(&MethodBindTester::UnaryByPointer, &object, &value)());
141  EXPECT_EQ(12, value);
142  EXPECT_EQ(7, object.call_count);
143  // It's possible to bind to a function that takes a const reference, though
144  // the capture will be a copy. See UnaryByRef hackery above where it removes
145  // the const to make sure the underlying storage is, in fact, a copy.
146  EXPECT_EQ(13, Bind(&MethodBindTester::UnaryByRef, &object, value)());
147  // But the original value is unmodified.
148  EXPECT_EQ(12, value);
149  EXPECT_EQ(8, object.call_count);
150  EXPECT_EQ(56, Bind(&MethodBindTester::Multiply, &object, 7, 8)());
151  EXPECT_EQ(9, object.call_count);
152}
153
154TEST(BindTest, BindToFunction) {
155  EXPECT_EQ(42, Bind(&Return42)());
156  EXPECT_EQ(3, Bind(&Negate, -3)());
157  EXPECT_EQ(56, Bind(&Multiply, 8, 7)());
158}
159
160// Test Bind where method object implements RefCountInterface and is passed as a
161// pointer.
162TEST(BindTest, CapturePointerAsScopedRefPtr) {
163  LifeTimeCheck object;
164  EXPECT_EQ(object.ref_count_, 0);
165  scoped_refptr<LifeTimeCheck> scoped_object(&object);
166  EXPECT_EQ(object.ref_count_, 1);
167  {
168    auto functor = Bind(&LifeTimeCheck::NullaryVoid, &object);
169    EXPECT_EQ(object.ref_count_, 2);
170    scoped_object = nullptr;
171    EXPECT_EQ(object.ref_count_, 1);
172  }
173  EXPECT_EQ(object.ref_count_, 0);
174}
175
176// Test Bind where method object implements RefCountInterface and is passed as a
177// scoped_refptr<>.
178TEST(BindTest, CaptureScopedRefPtrAsScopedRefPtr) {
179  LifeTimeCheck object;
180  EXPECT_EQ(object.ref_count_, 0);
181  scoped_refptr<LifeTimeCheck> scoped_object(&object);
182  EXPECT_EQ(object.ref_count_, 1);
183  {
184    auto functor = Bind(&LifeTimeCheck::NullaryVoid, scoped_object);
185    EXPECT_EQ(object.ref_count_, 2);
186    scoped_object = nullptr;
187    EXPECT_EQ(object.ref_count_, 1);
188  }
189  EXPECT_EQ(object.ref_count_, 0);
190}
191
192// Test Bind where method object is captured as scoped_refptr<> and the functor
193// dies while there are references left.
194TEST(BindTest, FunctorReleasesObjectOnDestruction) {
195  LifeTimeCheck object;
196  EXPECT_EQ(object.ref_count_, 0);
197  scoped_refptr<LifeTimeCheck> scoped_object(&object);
198  EXPECT_EQ(object.ref_count_, 1);
199  Bind(&LifeTimeCheck::NullaryVoid, &object)();
200  EXPECT_EQ(object.ref_count_, 1);
201  scoped_object = nullptr;
202  EXPECT_EQ(object.ref_count_, 0);
203}
204
205// Test Bind with scoped_refptr<> argument.
206TEST(BindTest, ScopedRefPointerArgument) {
207  LifeTimeCheck object;
208  EXPECT_EQ(object.ref_count_, 0);
209  scoped_refptr<LifeTimeCheck> scoped_object(&object);
210  EXPECT_EQ(object.ref_count_, 1);
211  {
212    MethodBindTester bind_tester;
213    auto functor =
214        Bind(&MethodBindTester::RefArgument, &bind_tester, scoped_object);
215    EXPECT_EQ(object.ref_count_, 2);
216  }
217  EXPECT_EQ(object.ref_count_, 1);
218  scoped_object = nullptr;
219  EXPECT_EQ(object.ref_count_, 0);
220}
221
222namespace {
223
224const int* Ref(const int& a) { return &a; }
225
226}  // anonymous namespace
227
228// Test Bind with non-scoped_refptr<> reference argument, which should be
229// modified to a non-reference capture.
230TEST(BindTest, RefArgument) {
231  const int x = 42;
232  EXPECT_EQ(&x, Ref(x));
233  // Bind() should make a copy of |x|, i.e. the pointers should be different.
234  auto functor = Bind(&Ref, x);
235  EXPECT_NE(&x, functor());
236}
237
238}  // namespace rtc
239