aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Auth/Tests/FIRAuthAPNSTokenManagerTests.m
blob: 79ffb7a7015e346b4f895b59244fdf3064b2f167 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
/*
 * Copyright 2017 Google
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import <XCTest/XCTest.h>

#import "FIRAuthAPNSToken.h"
#import "FIRAuthAPNSTokenManager.h"
#import <OCMock/OCMock.h>

NS_ASSUME_NONNULL_BEGIN

/** @var kRegistrationTimeout
    @brief The registration timeout used for testing.
 */
static const NSTimeInterval kRegistrationTimeout = .5;

/** @var kExpectationTimeout
    @brief The test expectation timeout.
    @remarks This must be considerably greater than @c kVerificationTimeout .
 */
static const NSTimeInterval kExpectationTimeout = 2;

/** @class FIRAuthLegacyUIApplication
    @brief A fake legacy (< iOS 7) UIApplication class.
    @remarks A custom class is needed because `respondsToSelector:` itself cannot be mocked.
 */
@interface FIRAuthLegacyUIApplication : NSObject

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)registerForRemoteNotificationTypes:(UIRemoteNotificationType)types;
#pragma clang diagnostic pop

@end
@implementation FIRAuthLegacyUIApplication

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)registerForRemoteNotificationTypes:(UIRemoteNotificationType)types {
}
#pragma clang diagnostic pop

@end

/** @class FIRAuthAPNSTokenManagerTests
    @brief Unit tests for @c FIRAuthAPNSTokenManager .
 */
@interface FIRAuthAPNSTokenManagerTests : XCTestCase
@end
@implementation FIRAuthAPNSTokenManagerTests {
  /** @var _mockApplication
      @brief The mock application for testing.
   */
  id _mockApplication;

  /** @var _manager
      @brief The @c FIRAuthAPNSTokenManager instance under tests.
   */
  FIRAuthAPNSTokenManager *_manager;

  /** @var _data
      @brief One piece of data used for testing.
   */
  NSData *_data;

  /** @var _otherData
      @brief Another piece of data used for testing.
   */
  NSData *_otherData;

  /** @var _error
      @brief The fake error used for testing.
   */
  NSError *_error;
}

- (void)setUp {
  _mockApplication = OCMClassMock([UIApplication class]);
  _manager = [[FIRAuthAPNSTokenManager alloc] initWithApplication:_mockApplication];
  _data = [@"qwerty" dataUsingEncoding:NSUTF8StringEncoding];
  _otherData = [@"!@#$" dataUsingEncoding:NSUTF8StringEncoding];
}

/** @fn testSetToken
    @brief Tests setting and getting the `token` property.
 */
- (void)testSetToken {
  XCTAssertNil(_manager.token);
  _manager.token = [[FIRAuthAPNSToken alloc] initWithData:_data type:FIRAuthAPNSTokenTypeProd];
  XCTAssertEqualObjects(_manager.token.data, _data);
  XCTAssertEqual(_manager.token.type, FIRAuthAPNSTokenTypeProd);
  _manager.token = nil;
  XCTAssertNil(_manager.token);
}

/** @fn testDetectTokenType
    @brief Tests automatic detection of token type.
 */
- (void)testDetectTokenType {
  XCTAssertNil(_manager.token);
  _manager.token = [[FIRAuthAPNSToken alloc] initWithData:_data type:FIRAuthAPNSTokenTypeUnknown];
  XCTAssertEqualObjects(_manager.token.data, _data);
  XCTAssertNotEqual(_manager.token.type, FIRAuthAPNSTokenTypeUnknown);
}

/** @fn testCallback
    @brief Tests callbacks are called.
 */
- (void)testCallback {
  // Add first callback, which is yet to be called.
  OCMExpect([_mockApplication registerForRemoteNotifications]);
  __block BOOL firstCallbackCalled = NO;
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertEqualObjects(token.data, _data);
    XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeSandbox);
    XCTAssertNil(error);
    firstCallbackCalled = YES;
  }];
  XCTAssertFalse(firstCallbackCalled);

  // Add second callback, which is yet to be called either.
  __block BOOL secondCallbackCalled = NO;
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertEqualObjects(token.data, _data);
    XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeSandbox);
    XCTAssertNil(error);
    secondCallbackCalled = YES;
  }];
  XCTAssertFalse(secondCallbackCalled);

  // Setting nil token shouldn't trigger either callbacks.
  _manager.token = nil;
  XCTAssertFalse(firstCallbackCalled);
  XCTAssertFalse(secondCallbackCalled);
  XCTAssertNil(_manager.token);

  // Setting a real token should trigger both callbacks.
  _manager.token = [[FIRAuthAPNSToken alloc] initWithData:_data type:FIRAuthAPNSTokenTypeSandbox];
  XCTAssertTrue(firstCallbackCalled);
  XCTAssertTrue(secondCallbackCalled);
  XCTAssertEqualObjects(_manager.token.data, _data);
  XCTAssertEqual(_manager.token.type, FIRAuthAPNSTokenTypeSandbox);

  // Add third callback, which should be called back immediately.
  __block BOOL thirdCallbackCalled = NO;
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertEqualObjects(token.data, _data);
    XCTAssertEqual(token.type, FIRAuthAPNSTokenTypeSandbox);
    XCTAssertNil(error);
    thirdCallbackCalled = YES;
  }];
  XCTAssertTrue(thirdCallbackCalled);

  // Verify the mock in the main thread.
  XCTestExpectation *expectation = [self expectationWithDescription:@"verify mock"];
  dispatch_async(dispatch_get_main_queue(), ^{
    OCMVerifyAll(_mockApplication);
    [expectation fulfill];
  });
  [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}

/** @fn testTimeout
    @brief Tests callbacks can be timed out.
 */
- (void)testTimeout {
  // Set up timeout.
  XCTAssertGreaterThan(_manager.timeout, 0);
  _manager.timeout = kRegistrationTimeout;

  // Add callback to time out.
  OCMExpect([_mockApplication registerForRemoteNotifications]);
  XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertNil(token);
    XCTAssertNil(error);
    [expectation fulfill];
  }];

  // Time out.
  [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  OCMVerifyAll(_mockApplication);

  // Calling cancel afterwards should have no effect.
  [_manager cancelWithError:_error];
}

/** @fn testCancel
    @brief Tests cancelling the pending callbacks.
 */
- (void)testCancel {
  // Set up timeout.
  XCTAssertGreaterThan(_manager.timeout, 0);
  _manager.timeout = kRegistrationTimeout;

  // Add callback to cancel.
  OCMExpect([_mockApplication registerForRemoteNotifications]);
  __block BOOL callbackCalled = NO;
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertNil(token);
    XCTAssertEqualObjects(error, _error);
    XCTAssertFalse(callbackCalled);  // verify callback is not called twice
    callbackCalled = YES;
  }];
  XCTAssertFalse(callbackCalled);

  // Call cancel.
  [_manager cancelWithError:_error];
  XCTAssertTrue(callbackCalled);

  // Add callback to timeout.
  XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertNil(token);
    XCTAssertNil(error);
    [expectation fulfill];
  }];

  // Time out.
  [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  OCMVerifyAll(_mockApplication);
}

/** @fn testLegacyRegistration
    @brief Tests remote notification registration on legacy systems.
 */
- (void)testLegacyRegistration {
  // Use a custom class for `respondsToSelector:` to work.
  _mockApplication = OCMClassMock([FIRAuthLegacyUIApplication class]);
  _manager = [[FIRAuthAPNSTokenManager alloc] initWithApplication:_mockApplication];

  // Add callback.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
  [[[_mockApplication expect] ignoringNonObjectArgs] registerForRemoteNotificationTypes:0];
#pragma clang diagnostic pop
  __block BOOL callbackCalled = NO;
  [_manager getTokenWithCallback:^(FIRAuthAPNSToken *_Nullable token, NSError *_Nullable error) {
    XCTAssertEqualObjects(token.data, _data);
    XCTAssertNotEqual(token.type, FIRAuthAPNSTokenTypeUnknown);
    XCTAssertNil(error);
    callbackCalled = YES;
  }];
  XCTAssertFalse(callbackCalled);

  // Set the token.
  _manager.token = [[FIRAuthAPNSToken alloc] initWithData:_data type:FIRAuthAPNSTokenTypeUnknown];
  XCTAssertTrue(callbackCalled);

  // Verify the mock in the main thread.
  XCTestExpectation *expectation = [self expectationWithDescription:@"verify mock"];
  dispatch_async(dispatch_get_main_queue(), ^{
    OCMVerifyAll(_mockApplication);
    [expectation fulfill];
  });
  [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
}

@end

NS_ASSUME_NONNULL_END