1// Copyright 2012 The Chromium Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <string> 6 7#include "base/functional/bind.h" 8#include "base/functional/callback.h" 9#include "base/functional/callback_helpers.h" 10#include "testing/gtest/include/gtest/gtest.h" 11#include "testing/gtest_mac.h" 12 13@interface CounterForBindObjcBlockTest : NSObject 14@property(nonatomic, assign) NSUInteger counter; 15@end 16 17@implementation CounterForBindObjcBlockTest 18@synthesize counter = _counter; 19@end 20 21@interface HelperForBindObjcBlockTest : NSObject 22 23- (instancetype)initWithCounter:(CounterForBindObjcBlockTest*)counter; 24- (void)incrementCounter; 25 26@end 27 28@implementation HelperForBindObjcBlockTest { 29 CounterForBindObjcBlockTest* _counter; 30} 31 32- (instancetype)initWithCounter:(CounterForBindObjcBlockTest*)counter { 33 if ((self = [super init])) { 34 _counter = counter; 35 DCHECK(_counter); 36 } 37 return self; 38} 39 40- (void)incrementCounter { 41 ++_counter.counter; 42} 43 44@end 45 46namespace { 47 48TEST(BindObjcBlockTest, TestScopedClosureRunnerExitScope) { 49 int run_count = 0; 50 int* ptr = &run_count; 51 { 52 base::ScopedClosureRunner runner(base::BindOnce(^{ 53 (*ptr)++; 54 })); 55 EXPECT_EQ(0, run_count); 56 } 57 EXPECT_EQ(1, run_count); 58} 59 60TEST(BindObjcBlockTest, TestScopedClosureRunnerRelease) { 61 int run_count = 0; 62 int* ptr = &run_count; 63 base::OnceClosure c; 64 { 65 base::ScopedClosureRunner runner(base::BindOnce(^{ 66 (*ptr)++; 67 })); 68 c = runner.Release(); 69 EXPECT_EQ(0, run_count); 70 } 71 EXPECT_EQ(0, run_count); 72 std::move(c).Run(); 73 EXPECT_EQ(1, run_count); 74} 75 76TEST(BindObjcBlockTest, TestReturnValue) { 77 const int kReturnValue = 42; 78 base::OnceCallback<int(void)> c = base::BindOnce(^{ 79 return kReturnValue; 80 }); 81 EXPECT_EQ(kReturnValue, std::move(c).Run()); 82} 83 84TEST(BindObjcBlockTest, TestArgument) { 85 const int kArgument = 42; 86 base::OnceCallback<int(int)> c = base::BindOnce(^(int a) { 87 return a + 1; 88 }); 89 EXPECT_EQ(kArgument + 1, std::move(c).Run(kArgument)); 90} 91 92TEST(BindObjcBlockTest, TestTwoArguments) { 93 std::string result; 94 std::string* ptr = &result; 95 base::OnceCallback<void(const std::string&, const std::string&)> c = 96 base::BindOnce(^(const std::string& a, const std::string& b) { 97 *ptr = a + b; 98 }); 99 std::move(c).Run("forty", "two"); 100 EXPECT_EQ(result, "fortytwo"); 101} 102 103TEST(BindObjcBlockTest, TestThreeArguments) { 104 std::string result; 105 std::string* ptr = &result; 106 base::OnceCallback<void(const std::string&, const std::string&, 107 const std::string&)> 108 cb = base::BindOnce( 109 ^(const std::string& a, const std::string& b, const std::string& c) { 110 *ptr = a + b + c; 111 }); 112 std::move(cb).Run("six", "times", "nine"); 113 EXPECT_EQ(result, "sixtimesnine"); 114} 115 116TEST(BindObjcBlockTest, TestSixArguments) { 117 std::string result1; 118 std::string* ptr = &result1; 119 int result2; 120 int* ptr2 = &result2; 121 base::OnceCallback<void(int, int, const std::string&, const std::string&, int, 122 const std::string&)> 123 cb = base::BindOnce(^(int a, int b, const std::string& c, 124 const std::string& d, int e, const std::string& f) { 125 *ptr = c + d + f; 126 *ptr2 = a + b + e; 127 }); 128 std::move(cb).Run(1, 2, "infinite", "improbability", 3, "drive"); 129 EXPECT_EQ(result1, "infiniteimprobabilitydrive"); 130 EXPECT_EQ(result2, 6); 131} 132 133TEST(BindObjcBlockTest, TestBlockMoveable) { 134 base::OnceClosure c; 135 __block BOOL invoked_block = NO; 136 @autoreleasepool { 137 c = base::BindOnce( 138 ^(std::unique_ptr<BOOL> v) { 139 invoked_block = *v; 140 }, 141 std::make_unique<BOOL>(YES)); 142 } 143 std::move(c).Run(); 144 EXPECT_TRUE(invoked_block); 145} 146 147// Tests that the bound block is retained until the end of its execution, even 148// if the callback itself is destroyed during the invocation. It was found that 149// some code depends on this behaviour (see https://crbug.com/845687). 150TEST(BindObjcBlockTest, TestBlockDeallocation) { 151 base::RepeatingClosure closure; 152 __block BOOL invoked_block = NO; 153 closure = base::BindRepeating( 154 ^(base::RepeatingClosure* this_closure) { 155 *this_closure = base::RepeatingClosure(); 156 invoked_block = YES; 157 }, 158 &closure); 159 closure.Run(); 160 EXPECT_TRUE(invoked_block); 161} 162 163TEST(BindObjcBlockTest, TestBlockReleased) { 164 __weak NSObject* weak_nsobject; 165 @autoreleasepool { 166 NSObject* nsobject = [[NSObject alloc] init]; 167 weak_nsobject = nsobject; 168 169 auto callback = base::BindOnce(^{ 170 [nsobject description]; 171 }); 172 } 173 EXPECT_NSEQ(nil, weak_nsobject); 174} 175 176// Tests that base::BindOnce(..., __strong NSObject*, ...) strongly captures 177// the Objective-C object. 178TEST(BindObjcBlockTest, TestBindOnceBoundStrongPointer) { 179 CounterForBindObjcBlockTest* counter = 180 [[CounterForBindObjcBlockTest alloc] init]; 181 ASSERT_EQ(counter.counter, 0u); 182 183 base::OnceClosure closure; 184 @autoreleasepool { 185 HelperForBindObjcBlockTest* helper = 186 [[HelperForBindObjcBlockTest alloc] initWithCounter:counter]; 187 188 // Creates a closure with a lambda taking the parameter as a __strong 189 // pointer and bound with a __strong pointer. This should retain the 190 // object. 191 closure = base::BindOnce( 192 [](HelperForBindObjcBlockTest* helper) { [helper incrementCounter]; }, 193 helper); 194 } 195 196 // Check that calling the closure increments the counter since the helper 197 // object was captured strongly and thus is retained by the closure. 198 std::move(closure).Run(); 199 EXPECT_EQ(counter.counter, 1u); 200} 201 202// Tests that base::BindOnce(..., __weak NSObject*, ...) weakly captures 203// the Objective-C object. 204TEST(BindObjcBlockTest, TestBindOnceBoundWeakPointer) { 205 CounterForBindObjcBlockTest* counter = 206 [[CounterForBindObjcBlockTest alloc] init]; 207 ASSERT_EQ(counter.counter, 0u); 208 209 base::OnceClosure closure; 210 @autoreleasepool { 211 HelperForBindObjcBlockTest* helper = 212 [[HelperForBindObjcBlockTest alloc] initWithCounter:counter]; 213 214 // Creates a closure with a lambda taking the parameter as a __strong 215 // pointer and bound with a __weak pointer. This should not retain the 216 // object. 217 __weak HelperForBindObjcBlockTest* weak_helper = helper; 218 closure = base::BindOnce( 219 [](HelperForBindObjcBlockTest* helper) { [helper incrementCounter]; }, 220 weak_helper); 221 } 222 223 // Check that calling the closure does not increment the counter since 224 // the helper object was captured weakly and thus is not retained by 225 // the closure. 226 std::move(closure).Run(); 227 EXPECT_EQ(counter.counter, 0u); 228} 229 230// Tests that base::BindRepeating(..., __strong NSObject*, ...) strongly 231// captures the Objective-C object. 232TEST(BindObjcBlockTest, TestBindRepeatingBoundStrongPointer) { 233 CounterForBindObjcBlockTest* counter = 234 [[CounterForBindObjcBlockTest alloc] init]; 235 ASSERT_EQ(counter.counter, 0u); 236 237 base::RepeatingClosure closure; 238 @autoreleasepool { 239 HelperForBindObjcBlockTest* helper = 240 [[HelperForBindObjcBlockTest alloc] initWithCounter:counter]; 241 242 // Creates a closure with a lambda taking the parameter as a __strong 243 // pointer and bound with a __strong pointer. This should retain the 244 // object. 245 closure = base::BindRepeating( 246 [](HelperForBindObjcBlockTest* helper) { [helper incrementCounter]; }, 247 helper); 248 } 249 250 // Check that calling the closure increments the counter since the helper 251 // object was captured strongly and thus is retained by the closure. 252 closure.Run(); 253 closure.Run(); 254 closure.Run(); 255 EXPECT_EQ(counter.counter, 3u); 256} 257 258// Tests that base::BindRepeating(..., __weak NSObject*, ...) weakly captures 259// the Objective-C object. 260TEST(BindObjcBlockTest, TestBindRepeatingBoundWeakPointer) { 261 CounterForBindObjcBlockTest* counter = 262 [[CounterForBindObjcBlockTest alloc] init]; 263 ASSERT_EQ(counter.counter, 0u); 264 265 base::RepeatingClosure closure; 266 @autoreleasepool { 267 HelperForBindObjcBlockTest* helper = 268 [[HelperForBindObjcBlockTest alloc] initWithCounter:counter]; 269 270 // Creates a closure with a lambda taking the parameter as a __strong 271 // pointer and bound with a __weak pointer. This should not retain the 272 // object. 273 __weak HelperForBindObjcBlockTest* weak_helper = helper; 274 closure = base::BindRepeating( 275 [](HelperForBindObjcBlockTest* helper) { [helper incrementCounter]; }, 276 weak_helper); 277 } 278 279 // Check that calling the closure does not increment the counter since 280 // the helper object was captured weakly and thus is not retained by 281 // the closure. 282 closure.Run(); 283 closure.Run(); 284 closure.Run(); 285 EXPECT_EQ(counter.counter, 0u); 286} 287 288} // namespace 289