FSTDispatchQueueTests.mm 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright 2018 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 "Firestore/Source/Util/FSTDispatchQueue.h"
  17. #import <XCTest/XCTest.h>
  18. #import "Firestore/Example/Tests/Util/XCTestCase+Await.h"
  19. // In these generic tests the specific TimerIDs don't matter.
  20. static const FSTTimerID timerID1 = FSTTimerIDListenStreamConnectionBackoff;
  21. static const FSTTimerID timerID2 = FSTTimerIDListenStreamIdle;
  22. static const FSTTimerID timerID3 = FSTTimerIDWriteStreamConnectionBackoff;
  23. @interface FSTDispatchQueueTests : XCTestCase
  24. @end
  25. @implementation FSTDispatchQueueTests {
  26. dispatch_queue_t _underlyingQueue;
  27. FSTDispatchQueue *_queue;
  28. NSMutableArray *_completedSteps;
  29. NSArray *_expectedSteps;
  30. XCTestExpectation *_expectation;
  31. }
  32. - (void)setUp {
  33. [super setUp];
  34. _underlyingQueue = dispatch_queue_create("FSTDispatchQueueTests", DISPATCH_QUEUE_SERIAL);
  35. _queue = [[FSTDispatchQueue alloc] initWithQueue:_underlyingQueue];
  36. _completedSteps = [NSMutableArray array];
  37. _expectedSteps = nil;
  38. }
  39. - (void)testDispatchAsyncBlocksSubmissionFromTasksOnTheQueue {
  40. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  41. __block NSException *caught = nil;
  42. __block NSString *problem = nil;
  43. [_queue dispatchAsync:^{
  44. @try {
  45. [self->_queue dispatchAsync:^{
  46. }];
  47. problem = @"Should have disallowed submission into the queue while running";
  48. [expectation fulfill];
  49. } @catch (NSException *ex) {
  50. caught = ex;
  51. [expectation fulfill];
  52. }
  53. }];
  54. [self awaitExpectations];
  55. XCTAssertNil(problem);
  56. XCTAssertNotNil(caught);
  57. XCTAssertEqualObjects(caught.name, NSInternalInconsistencyException);
  58. XCTAssertTrue(
  59. [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  60. @"dispatchAsync called when we are already running on target"]);
  61. }
  62. - (void)testDispatchAsyncAllowingSameQueueActuallyAllowsSameQueue {
  63. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  64. __block NSException *caught = nil;
  65. [_queue dispatchAsync:^{
  66. @try {
  67. [self->_queue dispatchAsyncAllowingSameQueue:^{
  68. [expectation fulfill];
  69. }];
  70. } @catch (NSException *ex) {
  71. caught = ex;
  72. [expectation fulfill];
  73. }
  74. }];
  75. [self awaitExpectations];
  76. XCTAssertNil(caught);
  77. }
  78. - (void)testDispatchAsyncAllowsSameQueueForUnownedActions {
  79. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  80. __block NSException *caught = nil;
  81. // Simulate the case of an action that runs on our queue because e.g. it's run by a user-owned
  82. // deinitializer that happened to be last held in one of our API methods.
  83. dispatch_async(_underlyingQueue, ^{
  84. @try {
  85. [self->_queue dispatchAsync:^{
  86. [expectation fulfill];
  87. }];
  88. } @catch (NSException *ex) {
  89. caught = ex;
  90. [expectation fulfill];
  91. }
  92. });
  93. [self awaitExpectations];
  94. XCTAssertNil(caught);
  95. }
  96. - (void)testDispatchSyncBlocksSubmissionFromTasksOnTheQueue {
  97. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  98. __block NSException *caught = nil;
  99. __block NSString *problem = nil;
  100. [_queue dispatchSync:^{
  101. @try {
  102. [self->_queue dispatchSync:^{
  103. }];
  104. problem = @"Should have disallowed submission into the queue while running";
  105. [expectation fulfill];
  106. } @catch (NSException *ex) {
  107. caught = ex;
  108. [expectation fulfill];
  109. }
  110. }];
  111. [self awaitExpectations];
  112. XCTAssertNil(problem);
  113. XCTAssertNotNil(caught);
  114. XCTAssertEqualObjects(caught.name, NSInternalInconsistencyException);
  115. XCTAssertTrue(
  116. [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  117. @"dispatchSync called when we are already running on target"]);
  118. }
  119. - (void)testVerifyIsCurrentQueueActuallyRequiresCurrentQueue {
  120. XCTAssertNotEqualObjects(_underlyingQueue, dispatch_get_main_queue());
  121. __block NSException *caught = nil;
  122. @try {
  123. // Run on the main queue not the FSTDispatchQueue's queue
  124. [_queue verifyIsCurrentQueue];
  125. } @catch (NSException *ex) {
  126. caught = ex;
  127. }
  128. XCTAssertNotNil(caught);
  129. XCTAssertTrue([caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  130. @"We are running on the wrong dispatch queue"]);
  131. }
  132. - (void)testVerifyIsCurrentQueueRequiresOperationIsInProgress {
  133. __block NSException *caught = nil;
  134. dispatch_sync(_underlyingQueue, ^{
  135. @try {
  136. [self->_queue verifyIsCurrentQueue];
  137. } @catch (NSException *ex) {
  138. caught = ex;
  139. }
  140. });
  141. XCTAssertNotNil(caught);
  142. XCTAssertTrue(
  143. [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  144. @"verifyIsCurrentQueue called outside enterCheckedOperation"]);
  145. }
  146. - (void)testVerifyIsCurrentQueueWorksWithOperationIsInProgress {
  147. __block NSException *caught = nil;
  148. [_queue dispatchSync:^{
  149. @try {
  150. [self->_queue verifyIsCurrentQueue];
  151. } @catch (NSException *ex) {
  152. caught = ex;
  153. }
  154. }];
  155. XCTAssertNil(caught);
  156. }
  157. - (void)testEnterCheckedOperationDisallowsNesting {
  158. __block NSException *caught = nil;
  159. __block NSString *problem = nil;
  160. [_queue dispatchSync:^{
  161. @try {
  162. [self->_queue enterCheckedOperation:^{
  163. }];
  164. problem = @"Should not have been able to enter nested enterCheckedOperation";
  165. } @catch (NSException *ex) {
  166. caught = ex;
  167. }
  168. }];
  169. XCTAssertNil(problem);
  170. XCTAssertNotNil(caught);
  171. XCTAssertTrue([caught.reason
  172. hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  173. @"enterCheckedOperation may not be called when an operation is in progress"]);
  174. }
  175. /**
  176. * Helper to return a block that adds @(n) to _completedSteps when run and fulfils _expectation if
  177. * the _completedSteps match the _expectedSteps.
  178. */
  179. - (void (^)())blockForStep:(int)n {
  180. return ^void() {
  181. [self->_completedSteps addObject:@(n)];
  182. if (self->_expectedSteps && self->_completedSteps.count >= self->_expectedSteps.count) {
  183. XCTAssertEqualObjects(self->_completedSteps, self->_expectedSteps);
  184. [self->_expectation fulfill];
  185. }
  186. };
  187. }
  188. - (void)testCanScheduleCallbacksInTheFuture {
  189. _expectation = [self expectationWithDescription:@"Expected steps"];
  190. _expectedSteps = @[ @1, @2, @3, @4 ];
  191. [_queue dispatchAsync:[self blockForStep:1]];
  192. [_queue dispatchAfterDelay:0.005 timerID:timerID1 block:[self blockForStep:4]];
  193. [_queue dispatchAfterDelay:0.001 timerID:timerID2 block:[self blockForStep:3]];
  194. [_queue dispatchAsync:[self blockForStep:2]];
  195. [self awaitExpectations];
  196. }
  197. - (void)testCanCancelDelayedCallbacks {
  198. _expectation = [self expectationWithDescription:@"Expected steps"];
  199. _expectedSteps = @[ @1, @3 ];
  200. // Queue everything from the queue to ensure nothing completes before we cancel.
  201. [_queue dispatchAsync:^{
  202. [self->_queue dispatchAsyncAllowingSameQueue:[self blockForStep:1]];
  203. FSTDelayedCallback *step2Timer =
  204. [self->_queue dispatchAfterDelay:.001 timerID:timerID1 block:[self blockForStep:2]];
  205. [self->_queue dispatchAfterDelay:.005 timerID:timerID2 block:[self blockForStep:3]];
  206. XCTAssertTrue([self->_queue containsDelayedCallbackWithTimerID:timerID1]);
  207. [step2Timer cancel];
  208. XCTAssertFalse([self->_queue containsDelayedCallbackWithTimerID:timerID1]);
  209. }];
  210. [self awaitExpectations];
  211. }
  212. - (void)testCanManuallyDrainAllDelayedCallbacksForTesting {
  213. [_queue dispatchAsync:[self blockForStep:1]];
  214. [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:4]];
  215. [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]];
  216. [_queue dispatchAsync:[self blockForStep:2]];
  217. [_queue runDelayedCallbacksUntil:FSTTimerIDAll];
  218. XCTAssertEqualObjects(_completedSteps, (@[ @1, @2, @3, @4 ]));
  219. }
  220. - (void)testCanManuallyDrainSpecificDelayedCallbacksForTesting {
  221. [_queue dispatchAsync:[self blockForStep:1]];
  222. [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:5]];
  223. [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]];
  224. [_queue dispatchAfterDelay:15 timerID:timerID3 block:[self blockForStep:4]];
  225. [_queue dispatchAsync:[self blockForStep:2]];
  226. [_queue runDelayedCallbacksUntil:timerID3];
  227. XCTAssertEqualObjects(_completedSteps, (@[ @1, @2, @3, @4 ]));
  228. }
  229. @end