aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m
diff options
context:
space:
mode:
authorGravatar Paul Beusterien <paulbeusterien@google.com>2017-05-15 12:27:07 -0700
committerGravatar Paul Beusterien <paulbeusterien@google.com>2017-05-15 12:27:07 -0700
commit98ba64449a632518bd2b86fe8d927f4a960d3ddc (patch)
tree131d9c4272fa6179fcda6c5a33fcb3b1bd57ad2e /Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m
parent32461366c9e204a527ca05e6e9b9404a2454ac51 (diff)
Initial
Diffstat (limited to 'Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m')
-rw-r--r--Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m263
1 files changed, 263 insertions, 0 deletions
diff --git a/Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m b/Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m
new file mode 100644
index 0000000..2033cb4
--- /dev/null
+++ b/Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m
@@ -0,0 +1,263 @@
+/*
+ * 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 <OCMock/OCMock.h>
+#import <XCTest/XCTest.h>
+
+#import "FIRMessagingDefines.h"
+#import "FIRMessagingPendingTopicsList.h"
+#import "FIRMessagingTopicsCommon.h"
+
+typedef void (^MockDelegateSubscriptionHandler)(NSString *topic,
+ FIRMessagingTopicAction action,
+ FIRMessagingTopicOperationCompletion completion);
+
+/**
+ * This object lets us provide a stub delegate where we can customize the behavior by providing
+ * blocks. We need to use this instead of stubbing a OCMockProtocol because our delegate methods
+ * take primitive values (e.g. action), which is not easy to use from OCMock
+ * @see http://stackoverflow.com/a/6332023
+ */
+@interface MockPendingTopicsListDelegate: NSObject <FIRMessagingPendingTopicsListDelegate>
+
+@property(nonatomic, assign) BOOL isReady;
+@property(nonatomic, copy) MockDelegateSubscriptionHandler subscriptionHandler;
+@property(nonatomic, copy) void(^updateHandler)();
+
+@end
+
+@implementation MockPendingTopicsListDelegate
+
+- (BOOL)pendingTopicsListCanRequestTopicUpdates:(FIRMessagingPendingTopicsList *)list {
+ return self.isReady;
+}
+
+- (void)pendingTopicsList:(FIRMessagingPendingTopicsList *)list
+ requestedUpdateForTopic:(NSString *)topic
+ action:(FIRMessagingTopicAction)action
+ completion:(FIRMessagingTopicOperationCompletion)completion {
+ if (self.subscriptionHandler) {
+ self.subscriptionHandler(topic, action, completion);
+ }
+}
+
+- (void)pendingTopicsListDidUpdate:(FIRMessagingPendingTopicsList *)list {
+ if (self.updateHandler) {
+ self.updateHandler();
+ }
+}
+
+@end
+
+@interface FIRMessagingPendingTopicsListTest : XCTestCase
+
+/// Using this delegate lets us prevent any topic operations from start, making it easy to measure
+/// our batches
+@property(nonatomic, strong) MockPendingTopicsListDelegate *notReadyDelegate;
+/// Using this delegate will always begin topic operations (which will never return by default).
+/// Useful for overriding with block-methods to handle update requests
+@property(nonatomic, strong) MockPendingTopicsListDelegate *alwaysReadyDelegate;
+
+@end
+
+@implementation FIRMessagingPendingTopicsListTest
+
+- (void)setUp {
+ [super setUp];
+ self.notReadyDelegate = [[MockPendingTopicsListDelegate alloc] init];
+ self.notReadyDelegate.isReady = NO;
+
+ self.alwaysReadyDelegate = [[MockPendingTopicsListDelegate alloc] init];
+ self.alwaysReadyDelegate.isReady = YES;
+}
+
+- (void)tearDown {
+ self.notReadyDelegate = nil;
+ self.alwaysReadyDelegate = nil;
+ [super tearDown];
+}
+
+- (void)testAddSingleTopic {
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.notReadyDelegate;
+
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ XCTAssertEqual(pendingTopics.numberOfBatches, 1);
+}
+
+- (void)testAddSameTopicAndActionMultipleTimes {
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.notReadyDelegate;
+
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ XCTAssertEqual(pendingTopics.numberOfBatches, 1);
+}
+
+- (void)testAddMultiplePendingTopicsWithSameAction {
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.notReadyDelegate;
+
+ for (NSInteger i = 0; i < 10; i++) {
+ NSString *topic = [NSString stringWithFormat:@"/topics/%ld", i];
+ [pendingTopics addOperationForTopic:topic
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ }
+ XCTAssertEqual(pendingTopics.numberOfBatches, 1);
+}
+
+- (void)testAddTopicsWithDifferentActions {
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.notReadyDelegate;
+
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/1"
+ withAction:FIRMessagingTopicActionUnsubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/2"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ XCTAssertEqual(pendingTopics.numberOfBatches, 3);
+}
+
+- (void)testBatchSizeReductionAfterSuccessfulTopicUpdate {
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.alwaysReadyDelegate;
+
+ XCTestExpectation *batchSizeReductionExpectation =
+ [self expectationWithDescription:@"Batch size was reduced after topic suscription"];
+
+ FIRMessaging_WEAKIFY(self)
+ self.alwaysReadyDelegate.subscriptionHandler =
+ ^(NSString *topic,
+ FIRMessagingTopicAction action,
+ FIRMessagingTopicOperationCompletion completion) {
+ // Simulate that the handler is generally called asynchronously
+ dispatch_async(dispatch_get_main_queue(), ^{
+ FIRMessaging_STRONGIFY(self)
+ if (action == FIRMessagingTopicActionUnsubscribe) {
+ XCTAssertEqual(pendingTopics.numberOfBatches, 1);
+ [batchSizeReductionExpectation fulfill];
+ }
+ completion(FIRMessagingTopicOperationResultSucceeded, nil);
+ });
+ };
+
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/1"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/2"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/1"
+ withAction:FIRMessagingTopicActionUnsubscribe
+ completion:nil];
+
+ [self waitForExpectationsWithTimeout:5.0 handler:nil];
+}
+
+- (void)testCompletionOfTopicUpdatesInSameThread {
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.alwaysReadyDelegate;
+
+ XCTestExpectation *allOperationsSucceededed =
+ [self expectationWithDescription:@"All queued operations succeeded"];
+
+ self.alwaysReadyDelegate.subscriptionHandler =
+ ^(NSString *topic,
+ FIRMessagingTopicAction action,
+ FIRMessagingTopicOperationCompletion completion) {
+ // Typically, our callbacks happen asynchronously, but to ensure resilience,
+ // call back the operation on the same thread it was called in.
+ completion(FIRMessagingTopicOperationResultSucceeded, nil);
+ };
+
+ self.alwaysReadyDelegate.updateHandler = ^{
+ if (pendingTopics.numberOfBatches == 0) {
+ [allOperationsSucceededed fulfill];
+ }
+ };
+
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/1"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ [pendingTopics addOperationForTopic:@"/topics/2"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+
+ [self waitForExpectationsWithTimeout:5.0 handler:nil];
+}
+
+- (void)testAddingTopicToCurrentBatchWhileCurrentBatchTopicsInFlight {
+
+ FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
+ pendingTopics.delegate = self.alwaysReadyDelegate;
+
+ NSString *stragglerTopic = @"/topics/straggler";
+ XCTestExpectation *stragglerTopicWasAddedToInFlightOperations =
+ [self expectationWithDescription:@"The topic was added to in-flight operations"];
+
+ self.alwaysReadyDelegate.subscriptionHandler =
+ ^(NSString *topic,
+ FIRMessagingTopicAction action,
+ FIRMessagingTopicOperationCompletion completion) {
+ if ([topic isEqualToString:stragglerTopic]) {
+ [stragglerTopicWasAddedToInFlightOperations fulfill];
+ }
+ // Add a 0.5 second delay to the completion, to give time to add a straggler before the batch
+ // is completed
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
+ dispatch_get_main_queue(),
+ ^{
+ completion(FIRMessagingTopicOperationResultSucceeded, nil);
+ });
+ };
+
+ // This is a normal topic, which should start fairly soon, but take a while to complete
+ [pendingTopics addOperationForTopic:@"/topics/0"
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ // While waiting for the first topic to complete, we add another topic after a slight delay
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)),
+ dispatch_get_main_queue(),
+ ^{
+ [pendingTopics addOperationForTopic:stragglerTopic
+ withAction:FIRMessagingTopicActionSubscribe
+ completion:nil];
+ });
+
+ [self waitForExpectationsWithTimeout:5.0 handler:nil];
+}
+
+@end