• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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