xref: /aosp_15_r20/external/cronet/base/ios/crb_protocol_observers_unittest.mm (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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