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