FIRMessagingPendingTopicsListTest.m 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 "Example/Messaging/Tests/FIRMessagingTestUtilities.h"
  19. #import "Firebase/Messaging/FIRMessagingDefines.h"
  20. #import "Firebase/Messaging/FIRMessagingPendingTopicsList.h"
  21. #import "Firebase/Messaging/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. FIRMessaging_WEAKIFY(self) self.alwaysReadyDelegate.subscriptionHandler =
  96. ^(NSString *topic, FIRMessagingTopicAction action,
  97. FIRMessagingTopicOperationCompletion completion) {
  98. // Simulate that the handler is generally called asynchronously
  99. dispatch_async(dispatch_get_main_queue(), ^{
  100. FIRMessaging_STRONGIFY(self) if (action == FIRMessagingTopicActionUnsubscribe) {
  101. XCTAssertEqual(pendingTopics.numberOfBatches, 1);
  102. [batchSizeReductionExpectation fulfill];
  103. }
  104. completion(nil);
  105. });
  106. };
  107. [pendingTopics addOperationForTopic:@"/topics/0"
  108. withAction:FIRMessagingTopicActionSubscribe
  109. completion:nil];
  110. [pendingTopics addOperationForTopic:@"/topics/1"
  111. withAction:FIRMessagingTopicActionSubscribe
  112. completion:nil];
  113. [pendingTopics addOperationForTopic:@"/topics/2"
  114. withAction:FIRMessagingTopicActionSubscribe
  115. completion:nil];
  116. [pendingTopics addOperationForTopic:@"/topics/1"
  117. withAction:FIRMessagingTopicActionUnsubscribe
  118. completion:nil];
  119. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  120. }
  121. - (void)testCompletionOfTopicUpdatesInSameThread {
  122. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  123. pendingTopics.delegate = self.alwaysReadyDelegate;
  124. XCTestExpectation *allOperationsSucceededed =
  125. [self expectationWithDescription:@"All queued operations succeeded"];
  126. self.alwaysReadyDelegate.subscriptionHandler =
  127. ^(NSString *topic, FIRMessagingTopicAction action,
  128. FIRMessagingTopicOperationCompletion completion) {
  129. // Typically, our callbacks happen asynchronously, but to ensure resilience,
  130. // call back the operation on the same thread it was called in.
  131. completion(nil);
  132. };
  133. self.alwaysReadyDelegate.updateHandler = ^{
  134. if (pendingTopics.numberOfBatches == 0) {
  135. [allOperationsSucceededed fulfill];
  136. }
  137. };
  138. [pendingTopics addOperationForTopic:@"/topics/0"
  139. withAction:FIRMessagingTopicActionSubscribe
  140. completion:nil];
  141. [pendingTopics addOperationForTopic:@"/topics/1"
  142. withAction:FIRMessagingTopicActionSubscribe
  143. completion:nil];
  144. [pendingTopics addOperationForTopic:@"/topics/2"
  145. withAction:FIRMessagingTopicActionSubscribe
  146. completion:nil];
  147. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  148. }
  149. - (void)testAddingTopicToCurrentBatchWhileCurrentBatchTopicsInFlight {
  150. FIRMessagingPendingTopicsList *pendingTopics = [[FIRMessagingPendingTopicsList alloc] init];
  151. pendingTopics.delegate = self.alwaysReadyDelegate;
  152. NSString *stragglerTopic = @"/topics/straggler";
  153. XCTestExpectation *stragglerTopicWasAddedToInFlightOperations =
  154. [self expectationWithDescription:@"The topic was added to in-flight operations"];
  155. self.alwaysReadyDelegate.subscriptionHandler =
  156. ^(NSString *topic, FIRMessagingTopicAction action,
  157. FIRMessagingTopicOperationCompletion completion) {
  158. if ([topic isEqualToString:stragglerTopic]) {
  159. [stragglerTopicWasAddedToInFlightOperations fulfill];
  160. }
  161. // Add a 0.5 second delay to the completion, to give time to add a straggler before the
  162. // batch is completed
  163. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
  164. dispatch_get_main_queue(), ^{
  165. completion(nil);
  166. });
  167. };
  168. // This is a normal topic, which should start fairly soon, but take a while to complete
  169. [pendingTopics addOperationForTopic:@"/topics/0"
  170. withAction:FIRMessagingTopicActionSubscribe
  171. completion:nil];
  172. // While waiting for the first topic to complete, we add another topic after a slight delay
  173. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)),
  174. dispatch_get_main_queue(), ^{
  175. [pendingTopics addOperationForTopic:stragglerTopic
  176. withAction:FIRMessagingTopicActionSubscribe
  177. completion:nil];
  178. });
  179. [self waitForExpectationsWithTimeout:5.0 handler:nil];
  180. }
  181. @end