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