1// Copyright 2014 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#import "base/ios/crb_protocol_observers.h" 6 7#include "base/notreached.h" 8#include "testing/gtest/include/gtest/gtest.h" 9#include "testing/gtest_mac.h" 10#include "testing/platform_test.h" 11 12#if !defined(__has_feature) || !__has_feature(objc_arc) 13#error "This file requires ARC support." 14#endif 15 16@protocol TestObserver 17 18@required 19- (void)requiredMethod; 20- (void)reset; 21 22@optional 23- (void)optionalMethod; 24- (void)mutateByAddingObserver:(id<TestObserver>)observer; 25- (void)mutateByRemovingObserver:(id<TestObserver>)observer; 26- (void)nestedMutateByAddingObserver:(id<TestObserver>)observer; 27- (void)nestedMutateByRemovingObserver:(id<TestObserver>)observer; 28 29@end 30 31// Implements only the required methods in the TestObserver protocol. 32@interface TestPartialObserver : NSObject<TestObserver> 33@property(nonatomic, readonly) BOOL requiredMethodInvoked; 34@end 35 36// Implements all the methods in the TestObserver protocol. 37@interface TestCompleteObserver : TestPartialObserver<TestObserver> 38@property(nonatomic, readonly) BOOL optionalMethodInvoked; 39@end 40 41@interface TestMutateObserver : TestCompleteObserver 42- (instancetype)initWithObserver:(CRBProtocolObservers*)observer 43 NS_DESIGNATED_INITIALIZER; 44- (instancetype)init NS_UNAVAILABLE; 45@end 46 47namespace { 48 49class CRBProtocolObserversTest : public PlatformTest { 50 public: 51 CRBProtocolObserversTest() {} 52 53 protected: 54 void SetUp() override { 55 PlatformTest::SetUp(); 56 57 observers_ = (CRBProtocolObservers<TestObserver>*)[CRBProtocolObservers 58 observersWithProtocol:@protocol(TestObserver)]; 59 60 partial_observer_ = [[TestPartialObserver alloc] init]; 61 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 62 63 complete_observer_ = [[TestCompleteObserver alloc] init]; 64 EXPECT_FALSE([complete_observer_ requiredMethodInvoked]); 65 EXPECT_FALSE([complete_observer_ optionalMethodInvoked]); 66 67 mutate_observer_ = [[TestMutateObserver alloc] initWithObserver:observers_]; 68 EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]); 69 } 70 71 CRBProtocolObservers<TestObserver>* observers_; 72 TestPartialObserver* partial_observer_; 73 TestCompleteObserver* complete_observer_; 74 TestMutateObserver* mutate_observer_; 75}; 76 77// Verifies basic functionality of -[CRBProtocolObservers addObserver:] and 78// -[CRBProtocolObservers removeObserver:]. 79TEST_F(CRBProtocolObserversTest, AddRemoveObserver) { 80 // Add an observer and verify that the CRBProtocolObservers instance forwards 81 // an invocation to it. 82 [observers_ addObserver:partial_observer_]; 83 [observers_ requiredMethod]; 84 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 85 86 [partial_observer_ reset]; 87 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 88 89 // Remove the observer and verify that the CRBProtocolObservers instance no 90 // longer forwards an invocation to it. 91 [observers_ removeObserver:partial_observer_]; 92 [observers_ requiredMethod]; 93 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 94} 95 96// Verifies that CRBProtocolObservers correctly forwards the invocation of a 97// required method in the protocol. 98TEST_F(CRBProtocolObserversTest, RequiredMethods) { 99 [observers_ addObserver:partial_observer_]; 100 [observers_ addObserver:complete_observer_]; 101 [observers_ requiredMethod]; 102 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 103 EXPECT_TRUE([complete_observer_ requiredMethodInvoked]); 104} 105 106// Verifies that CRBProtocolObservers correctly forwards the invocation of an 107// optional method in the protocol. 108TEST_F(CRBProtocolObserversTest, OptionalMethods) { 109 [observers_ addObserver:partial_observer_]; 110 [observers_ addObserver:complete_observer_]; 111 [observers_ optionalMethod]; 112 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 113 EXPECT_FALSE([complete_observer_ requiredMethodInvoked]); 114 EXPECT_TRUE([complete_observer_ optionalMethodInvoked]); 115} 116 117// Verifies that CRBProtocolObservers only holds a weak reference to an 118// observer. 119TEST_F(CRBProtocolObserversTest, WeakReference) { 120 __weak TestPartialObserver* weak_observer = partial_observer_; 121 EXPECT_TRUE(weak_observer); 122 123 [observers_ addObserver:partial_observer_]; 124 125 // Need an autorelease pool here, because 126 // -[CRBProtocolObservers forwardInvocation:] creates a temporary 127 // autoreleased array that holds all the observers. 128 @autoreleasepool { 129 [observers_ requiredMethod]; 130 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 131 partial_observer_ = nil; 132 } 133 134 EXPECT_FALSE(weak_observer); 135} 136 137// Verifies that an observer can safely remove itself as observer while being 138// notified. 139TEST_F(CRBProtocolObserversTest, SelfMutateObservers) { 140 [observers_ addObserver:mutate_observer_]; 141 EXPECT_FALSE([observers_ empty]); 142 143 [observers_ requiredMethod]; 144 EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); 145 146 [mutate_observer_ reset]; 147 148 [observers_ nestedMutateByRemovingObserver:mutate_observer_]; 149 EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]); 150 151 [observers_ addObserver:partial_observer_]; 152 153 [observers_ requiredMethod]; 154 EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]); 155 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 156 157 [observers_ removeObserver:partial_observer_]; 158 EXPECT_TRUE([observers_ empty]); 159} 160 161// Verifies that - [CRBProtocolObservers addObserver:] and 162// - [CRBProtocolObservers removeObserver:] can be called while methods are 163// being forwarded. 164TEST_F(CRBProtocolObserversTest, MutateObservers) { 165 // Indirectly add an observer while forwarding an observer method. 166 [observers_ addObserver:mutate_observer_]; 167 168 [observers_ mutateByAddingObserver:partial_observer_]; 169 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 170 171 // Check that methods are correctly forwared to the indirectly added observer. 172 [mutate_observer_ reset]; 173 [observers_ requiredMethod]; 174 EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); 175 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 176 177 [mutate_observer_ reset]; 178 [partial_observer_ reset]; 179 180 // Indirectly remove an observer while forwarding an observer method. 181 [observers_ mutateByRemovingObserver:partial_observer_]; 182 183 // Check that method is not forwared to the indirectly removed observer. 184 [observers_ requiredMethod]; 185 EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); 186 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 187} 188 189// Verifies that - [CRBProtocolObservers addObserver:] and 190// - [CRBProtocolObservers removeObserver:] can be called while methods are 191// being forwarded with a nested invocation depth > 0. 192TEST_F(CRBProtocolObserversTest, NestedMutateObservers) { 193 // Indirectly add an observer while forwarding an observer method. 194 [observers_ addObserver:mutate_observer_]; 195 196 [observers_ nestedMutateByAddingObserver:partial_observer_]; 197 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 198 199 // Check that methods are correctly forwared to the indirectly added observer. 200 [mutate_observer_ reset]; 201 [observers_ requiredMethod]; 202 EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); 203 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 204 205 [mutate_observer_ reset]; 206 [partial_observer_ reset]; 207 208 // Indirectly remove an observer while forwarding an observer method. 209 [observers_ nestedMutateByRemovingObserver:partial_observer_]; 210 211 // Check that method is not forwared to the indirectly removed observer. 212 [observers_ requiredMethod]; 213 EXPECT_TRUE([mutate_observer_ requiredMethodInvoked]); 214 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]); 215} 216 217// Verifies that CRBProtocolObservers works if an observer deallocs. 218TEST_F(CRBProtocolObserversTest, IgnoresDeallocedObservers) { 219 __weak TestPartialObserver* weak_observer = partial_observer_; 220 EXPECT_TRUE(weak_observer); 221 222 [observers_ addObserver:partial_observer_]; 223 224 // Need an autorelease pool here, because 225 // -[CRBProtocolObservers forwardInvocation:] creates a temporary 226 // autoreleased array that holds all the observers. 227 @autoreleasepool { 228 [observers_ requiredMethod]; 229 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]); 230 partial_observer_ = nil; 231 } 232 233 EXPECT_FALSE(weak_observer); 234 // This shouldn't crash. 235 [observers_ requiredMethod]; 236} 237 238} // namespace 239 240@implementation TestPartialObserver { 241 BOOL _requiredMethodInvoked; 242} 243 244- (BOOL)requiredMethodInvoked { 245 return _requiredMethodInvoked; 246} 247 248- (void)requiredMethod { 249 _requiredMethodInvoked = YES; 250} 251 252- (void)reset { 253 _requiredMethodInvoked = NO; 254} 255 256@end 257 258@implementation TestCompleteObserver { 259 BOOL _optionalMethodInvoked; 260} 261 262- (BOOL)optionalMethodInvoked { 263 return _optionalMethodInvoked; 264} 265 266- (void)optionalMethod { 267 _optionalMethodInvoked = YES; 268} 269 270- (void)reset { 271 [super reset]; 272 _optionalMethodInvoked = NO; 273} 274 275@end 276 277@implementation TestMutateObserver { 278 __weak id _observers; 279} 280 281- (instancetype)initWithObserver:(CRBProtocolObservers*)observers { 282 self = [super init]; 283 if (self) { 284 _observers = observers; 285 } 286 return self; 287} 288 289- (instancetype)init { 290 NOTREACHED(); 291 return nil; 292} 293 294- (void)mutateByAddingObserver:(id<TestObserver>)observer { 295 [_observers addObserver:observer]; 296} 297 298- (void)mutateByRemovingObserver:(id<TestObserver>)observer { 299 [_observers removeObserver:observer]; 300} 301 302- (void)nestedMutateByAddingObserver:(id<TestObserver>)observer { 303 [_observers mutateByAddingObserver:observer]; 304} 305 306- (void)nestedMutateByRemovingObserver:(id<TestObserver>)observer { 307 [_observers mutateByRemovingObserver:observer]; 308} 309 310@end 311