diff options
author | Paul Beusterien <paulbeusterien@google.com> | 2017-05-15 12:27:07 -0700 |
---|---|---|
committer | Paul Beusterien <paulbeusterien@google.com> | 2017-05-15 12:27:07 -0700 |
commit | 98ba64449a632518bd2b86fe8d927f4a960d3ddc (patch) | |
tree | 131d9c4272fa6179fcda6c5a33fcb3b1bd57ad2e /Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m | |
parent | 32461366c9e204a527ca05e6e9b9404a2454ac51 (diff) |
Initial
Diffstat (limited to 'Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m')
-rw-r--r-- | Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m | 263 |
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 |