blob: 546828e32b22995c84dd8dd0bb2c64df2aa5ad09 [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2014 The Chromium Authors
stuartmorgan4733f552015-02-14 22:19:302// 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"
Sylvain Defresneb20b7d52019-11-22 13:23:006
Hans Wennborga47ddf82020-05-05 18:08:077#include "base/notreached.h"
stuartmorgan4733f552015-02-14 22:19:308#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;
jbbegue12d50e72015-06-05 08:12:4320- (void)mutateByAddingObserver:(id<TestObserver>)observer;
21- (void)mutateByRemovingObserver:(id<TestObserver>)observer;
22- (void)nestedMutateByAddingObserver:(id<TestObserver>)observer;
23- (void)nestedMutateByRemovingObserver:(id<TestObserver>)observer;
stuartmorgan4733f552015-02-14 22:19:3024
25@end
26
27// Implements only the required methods in the TestObserver protocol.
Peter Kasting134ef9af2024-12-28 02:30:0928@interface TestPartialObserver : NSObject <TestObserver>
stuartmorgan4733f552015-02-14 22:19:3029@property(nonatomic, readonly) BOOL requiredMethodInvoked;
30@end
31
32// Implements all the methods in the TestObserver protocol.
Peter Kasting134ef9af2024-12-28 02:30:0933@interface TestCompleteObserver : TestPartialObserver <TestObserver>
stuartmorgan4733f552015-02-14 22:19:3034@property(nonatomic, readonly) BOOL optionalMethodInvoked;
35@end
36
jbbegue12d50e72015-06-05 08:12:4337@interface TestMutateObserver : TestCompleteObserver
jbbegue12d50e72015-06-05 08:12:4338- (instancetype)initWithObserver:(CRBProtocolObservers*)observer
39 NS_DESIGNATED_INITIALIZER;
justincohen5a8629932015-06-10 17:38:1640- (instancetype)init NS_UNAVAILABLE;
jbbegue12d50e72015-06-05 08:12:4341@end
42
stuartmorgan4733f552015-02-14 22:19:3043namespace {
44
45class CRBProtocolObserversTest : public PlatformTest {
46 public:
Sorin Jianu1ed1f6c2024-09-28 00:31:4047 CRBProtocolObserversTest() = default;
stuartmorgan4733f552015-02-14 22:19:3048
49 protected:
50 void SetUp() override {
51 PlatformTest::SetUp();
52
Stepan Khapugin81f3a642021-02-26 11:42:1353 observers_ = (CRBProtocolObservers<TestObserver>*)[CRBProtocolObservers
54 observersWithProtocol:@protocol(TestObserver)];
stuartmorgan4733f552015-02-14 22:19:3055
Stepan Khapugin81f3a642021-02-26 11:42:1356 partial_observer_ = [[TestPartialObserver alloc] init];
stuartmorgan4733f552015-02-14 22:19:3057 EXPECT_FALSE([partial_observer_ requiredMethodInvoked]);
58
Stepan Khapugin81f3a642021-02-26 11:42:1359 complete_observer_ = [[TestCompleteObserver alloc] init];
stuartmorgan4733f552015-02-14 22:19:3060 EXPECT_FALSE([complete_observer_ requiredMethodInvoked]);
61 EXPECT_FALSE([complete_observer_ optionalMethodInvoked]);
jbbegue12d50e72015-06-05 08:12:4362
Stepan Khapugin81f3a642021-02-26 11:42:1363 mutate_observer_ = [[TestMutateObserver alloc] initWithObserver:observers_];
jbbegue12d50e72015-06-05 08:12:4364 EXPECT_FALSE([mutate_observer_ requiredMethodInvoked]);
stuartmorgan4733f552015-02-14 22:19:3065 }
66
Stepan Khapugin81f3a642021-02-26 11:42:1367 CRBProtocolObservers<TestObserver>* observers_;
68 TestPartialObserver* partial_observer_;
69 TestCompleteObserver* complete_observer_;
70 TestMutateObserver* mutate_observer_;
stuartmorgan4733f552015-02-14 22:19:3071};
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) {
Stepan Khapugin81f3a642021-02-26 11:42:13116 __weak TestPartialObserver* weak_observer = partial_observer_;
stuartmorgan4733f552015-02-14 22:19:30117 EXPECT_TRUE(weak_observer);
118
119 [observers_ addObserver:partial_observer_];
120
Avi Drissman91160c632019-09-06 19:24:58121 // Need an autorelease pool here, because
122 // -[CRBProtocolObservers forwardInvocation:] creates a temporary
123 // autoreleased array that holds all the observers.
124 @autoreleasepool {
stuartmorgan4733f552015-02-14 22:19:30125 [observers_ requiredMethod];
126 EXPECT_TRUE([partial_observer_ requiredMethodInvoked]);
Stepan Khapugin81f3a642021-02-26 11:42:13127 partial_observer_ = nil;
stuartmorgan4733f552015-02-14 22:19:30128 }
129
Stepan Khapugin81f3a642021-02-26 11:42:13130 EXPECT_FALSE(weak_observer);
stuartmorgan4733f552015-02-14 22:19:30131}
132
jbbegue12d50e72015-06-05 08:12:43133// 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
Stepan Khapugin81f3a642021-02-26 11:42:13213// 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
stuartmorgan4733f552015-02-14 22:19:30234} // 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
jbbegue12d50e72015-06-05 08:12:43272
273@implementation TestMutateObserver {
Stepan Khapugin81f3a642021-02-26 11:42:13274 __weak id _observers;
jbbegue12d50e72015-06-05 08:12:43275}
276
277- (instancetype)initWithObserver:(CRBProtocolObservers*)observers {
278 self = [super init];
279 if (self) {
280 _observers = observers;
281 }
282 return self;
283}
284
justincohen5a8629932015-06-10 17:38:16285- (instancetype)init {
Peter Boströmde573332024-08-26 20:42:45286 NOTREACHED();
justincohen5a8629932015-06-10 17:38:16287}
288
jbbegue12d50e72015-06-05 08:12:43289- (void)mutateByAddingObserver:(id<TestObserver>)observer {
290 [_observers addObserver:observer];
291}
292
293- (void)mutateByRemovingObserver:(id<TestObserver>)observer {
294 [_observers removeObserver:observer];
295}
296
297- (void)nestedMutateByAddingObserver:(id<TestObserver>)observer {
298 [_observers mutateByAddingObserver:observer];
299}
300
301- (void)nestedMutateByRemovingObserver:(id<TestObserver>)observer {
302 [_observers mutateByRemovingObserver:observer];
303}
304
305@end