FIRMessagingPendingTopicsListTest.m 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <OCMock/OCMock.h>
  17. #import <XCTest/XCTest.h>
  18. #import "FirebaseMessaging/Tests/UnitTests/FIRMessagingTestUtilities.h"
  19. #import "FirebaseMessaging/Sources/FIRMessagingDefines.h"
  20. #import "FirebaseMessaging/Sources/FIRMessagingPendingTopicsList.h"
  21. #import "FirebaseMessaging/Sources/FIRMessagingTopicsCommon.h"
  22. @interface FIRMessagingPendingTopicsListTest : XCTestCase
  23. /// Using this delegate lets us prevent any topic operations from start, making it easy to measure
  24. /// our batches
  25. @property(nonatomic, strong) MockPendingTopicsListDelegate *notReadyDelegate;
  26. /// Using this delegate will always begin topic operations (which will never return by default).
  27. /// Useful for overriding with block-methods to handle update requests
  28. @property(nonatomic, strong) MockPendingTopicsListDelegate *alwaysReadyDelegate;
  29. @end
  30. @implementation FIRMessagingPendingTopicsListTest
  31. - (void)setUp {
  32. [super setUp];
  33. self.notReadyDelegate = [[MockPendingTopicsListDelegate alloc] init];
  34. self.notReadyDelegate.isReady = NO;
  35. self.alwaysReadyDelegate = [[MockPendingTopicsListDelegate alloc] init];
  36. self.alwaysReadyDelegate.isReady = YES;
  37. }
  38. - (void)tearDown {
  39. self.notReadyDelegate = nil;
  40. self.alwaysReadyDelegate = nil;
  41. [super tearDown];
  42. }
  43. - (void)testAddSingleTopic {
  44. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  45. pendingTopics.delegate = self.notReadyDelegate;
  46. [pendingTopics addOperationForTopic:@"/topics/0"
  47. withAction:FIRMessagingTopicActionSubscribe
  48. completion:nil];
  49. XCTAssertEqual(pendingTopics.numberOfBatches, 1);
  50. }
  51. - (void)testAddSameTopicAndActionMultipleTimes {
  52. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  53. pendingTopics.delegate = self.notReadyDelegate;
  54. [pendingTopics addOperationForTopic:@"/topics/0"
  55. withAction:FIRMessagingTopicActionSubscribe
  56. completion:nil];
  57. [pendingTopics addOperationForTopic:@"/topics/0"
  58. withAction:FIRMessagingTopicActionSubscribe
  59. completion:nil];
  60. [pendingTopics addOperationForTopic:@"/topics/0"
  61. withAction:FIRMessagingTopicActionSubscribe
  62. completion:nil];
  63. XCTAssertEqual(pendingTopics.numberOfBatches, 1);
  64. }
  65. - (void)testAddMultiplePendingTopicsWithSameAction {
  66. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  67. pendingTopics.delegate = self.notReadyDelegate;
  68. for (NSInteger i = 0; i < 10; i++) {
  69. NSString *topic = [NSString stringWithFormat:@"/topics/%ld", (long)i];
  70. [pendingTopics addOperationForTopic:topic
  71. withAction:FIRMessagingTopicActionSubscribe
  72. completion:nil];
  73. }
  74. XCTAssertEqual(pendingTopics.numberOfBatches, 1);
  75. }
  76. - (void)testAddTopicsWithDifferentActions {
  77. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  78. pendingTopics.delegate = self.notReadyDelegate;
  79. [pendingTopics addOperationForTopic:@"/topics/0"
  80. withAction:FIRMessagingTopicActionSubscribe
  81. completion:nil];
  82. [pendingTopics addOperationForTopic:@"/topics/1"
  83. withAction:FIRMessagingTopicActionUnsubscribe
  84. completion:nil];
  85. [pendingTopics addOperationForTopic:@"/topics/2"
  86. withAction:FIRMessagingTopicActionSubscribe
  87. completion:nil];
  88. XCTAssertEqual(pendingTopics.numberOfBatches, 3);
  89. }
  90. - (void)testBatchSizeReductionAfterSuccessfulTopicUpdate {
  91. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  92. pendingTopics.delegate = self.alwaysReadyDelegate;
  93. XCTestExpectation *batchSizeReductionExpectation =
  94. [self expectationWithDescription:@"Batch size was reduced after topic suscription"];
  95. __weak id weakSelf = self;
  96. self.alwaysReadyDelegate.subscriptionHandler =
  97. ^(NSString *topic, FIRMessagingTopicAction action,
  98. FIRMessagingTopicOperationCompletion completion) {
  99. // Simulate that the handler is generally called asynchronously
  100. dispatch_async(dispatch_get_main_queue(), ^{
  101. if (action == FIRMessagingTopicActionUnsubscribe) {
  102. __unused id self = weakSelf; // In Xcode 11, XCTAssertEqual references self.
  103. XCTAssertEqual(pendingTopics.numberOfBatches, 1);
  104. [batchSizeReductionExpectation fulfill];
  105. }
  106. completion(nil);
  107. });
  108. };
  109. [pendingTopics addOperationForTopic:@"/topics/0"
  110. withAction:FIRMessagingTopicActionSubscribe
  111. completion:nil];
  112. [pendingTopics addOperationForTopic:@"/topics/1"
  113. withAction:FIRMessagingTopicActionSubscribe
  114. completion:nil];
  115. [pendingTopics addOperationForTopic:@"/topics/2"
  116. withAction:FIRMessagingTopicActionSubscribe
  117. completion:nil];
  118. [pendingTopics addOperationForTopic:@"/topics/1"
  119. withAction:FIRMessagingTopicActionUnsubscribe
  120. completion:nil];
  121. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  122. }
  123. - (void)testCompletionOfTopicUpdatesInSameThread {
  124. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  125. pendingTopics.delegate = self.alwaysReadyDelegate;
  126. XCTestExpectation *allOperationsSucceededed =
  127. [self expectationWithDescription:@"All queued operations succeeded"];
  128. self.alwaysReadyDelegate.subscriptionHandler =
  129. ^(NSString *topic, FIRMessagingTopicAction action,
  130. FIRMessagingTopicOperationCompletion completion) {
  131. // Typically, our callbacks happen asynchronously, but to ensure resilience,
  132. // call back the operation on the same thread it was called in.
  133. completion(nil);
  134. };
  135. self.alwaysReadyDelegate.updateHandler = ^{
  136. if (pendingTopics.numberOfBatches == 0) {
  137. [allOperationsSucceededed fulfill];
  138. }
  139. };
  140. [pendingTopics addOperationForTopic:@"/topics/0"
  141. withAction:FIRMessagingTopicActionSubscribe
  142. completion:nil];
  143. [pendingTopics addOperationForTopic:@"/topics/1"
  144. withAction:FIRMessagingTopicActionSubscribe
  145. completion:nil];
  146. [pendingTopics addOperationForTopic:@"/topics/2"
  147. withAction:FIRMessagingTopicActionSubscribe
  148. completion:nil];
  149. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  150. }
  151. - (void)testAddingTopicToCurrentBatchWhileCurrentBatchTopicsInFlight {
  152. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  153. pendingTopics.delegate = self.alwaysReadyDelegate;
  154. NSString *stragglerTopic = @"/topics/straggler";
  155. XCTestExpectation *stragglerTopicWasAddedToInFlightOperations =
  156. [self expectationWithDescription:@"The topic was added to in-flight operations"];
  157. self.alwaysReadyDelegate.subscriptionHandler =
  158. ^(NSString *topic, FIRMessagingTopicAction action,
  159. FIRMessagingTopicOperationCompletion completion) {
  160. if ([topic isEqualToString:stragglerTopic]) {
  161. [stragglerTopicWasAddedToInFlightOperations fulfill];
  162. }
  163. // Add a 0.5 second delay to the completion, to give time to add a straggler before the
  164. // batch is completed
  165. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
  166. dispatch_get_main_queue(), ^{
  167. completion(nil);
  168. });
  169. };
  170. // This is a normal topic, which should start fairly soon, but take a while to complete
  171. [pendingTopics addOperationForTopic:@"/topics/0"
  172. withAction:FIRMessagingTopicActionSubscribe
  173. completion:nil];
  174. // While waiting for the first topic to complete, we add another topic after a slight delay
  175. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)),
  176. dispatch_get_main_queue(), ^{
  177. [pendingTopics addOperationForTopic:stragglerTopic
  178. withAction:FIRMessagingTopicActionSubscribe
  179. completion:nil];
  180. });
  181. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  182. }
  183. @end