Files
firebase-ios-sdk/Example/Messaging/Tests/FIRMessagingPendingTopicsListTest.m
T
Chen Liang 653aea7b50 Adding a new topic subscription/unsubcription with completion handler method (#1001)
* add new topic subscription/unsubscription method with handler
2018-03-30 11:41:38 -07:00

263 lines
10 KiB
Objective-C

/*
* 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(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(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(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