FSTDispatchQueueTests.mm 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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([caught.reason
  59. hasPrefix:
  60. @"FIRESTORE INTERNAL ASSERTION FAILED: "
  61. @"Enqueue methods cannot be called when we are already running on target executor"]);
  62. }
  63. - (void)testDispatchAsyncAllowingSameQueueActuallyAllowsSameQueue {
  64. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  65. __block NSException *caught = nil;
  66. [_queue dispatchAsync:^{
  67. @try {
  68. [self->_queue dispatchAsyncAllowingSameQueue:^{
  69. [expectation fulfill];
  70. }];
  71. } @catch (NSException *ex) {
  72. caught = ex;
  73. [expectation fulfill];
  74. }
  75. }];
  76. [self awaitExpectations];
  77. XCTAssertNil(caught);
  78. }
  79. - (void)testDispatchAsyncAllowsSameQueueForUnownedActions {
  80. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  81. __block NSException *caught = nil;
  82. // Simulate the case of an action that runs on our queue because e.g. it's run by a user-owned
  83. // deinitializer that happened to be last held in one of our API methods.
  84. dispatch_async(_underlyingQueue, ^{
  85. @try {
  86. [self->_queue dispatchAsync:^{
  87. [expectation fulfill];
  88. }];
  89. } @catch (NSException *ex) {
  90. caught = ex;
  91. [expectation fulfill];
  92. }
  93. });
  94. [self awaitExpectations];
  95. XCTAssertNil(caught);
  96. }
  97. - (void)testDispatchSyncBlocksSubmissionFromTasksOnTheQueue {
  98. XCTestExpectation *expectation = [self expectationWithDescription:@"completion"];
  99. __block NSException *caught = nil;
  100. __block NSString *problem = nil;
  101. [_queue dispatchSync:^{
  102. @try {
  103. [self->_queue dispatchSync:^{
  104. }];
  105. problem = @"Should have disallowed submission into the queue while running";
  106. [expectation fulfill];
  107. } @catch (NSException *ex) {
  108. caught = ex;
  109. [expectation fulfill];
  110. }
  111. }];
  112. [self awaitExpectations];
  113. XCTAssertNil(problem);
  114. XCTAssertNotNil(caught);
  115. XCTAssertEqualObjects(caught.name, NSInternalInconsistencyException);
  116. XCTAssertTrue([caught.reason
  117. hasPrefix:
  118. @"FIRESTORE INTERNAL ASSERTION FAILED: "
  119. @"Enqueue methods cannot be called when we are already running on target executor"]);
  120. }
  121. - (void)testVerifyIsCurrentQueueActuallyRequiresCurrentQueue {
  122. XCTAssertNotEqualObjects(_underlyingQueue, dispatch_get_main_queue());
  123. __block NSException *caught = nil;
  124. @try {
  125. // Run on the main queue not the FSTDispatchQueue's queue
  126. [_queue verifyIsCurrentQueue];
  127. } @catch (NSException *ex) {
  128. caught = ex;
  129. }
  130. XCTAssertNotNil(caught);
  131. XCTAssertTrue([caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  132. @"Expected to be called by the executor "
  133. @"associated with this queue"]);
  134. }
  135. - (void)testVerifyIsCurrentQueueRequiresOperationIsInProgress {
  136. __block NSException *caught = nil;
  137. dispatch_sync(_underlyingQueue, ^{
  138. @try {
  139. [self->_queue verifyIsCurrentQueue];
  140. } @catch (NSException *ex) {
  141. caught = ex;
  142. }
  143. });
  144. XCTAssertNotNil(caught);
  145. XCTAssertTrue(
  146. [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  147. @"VerifyIsCurrentQueue called when no operation is executing"]);
  148. }
  149. - (void)testVerifyIsCurrentQueueWorksWithOperationIsInProgress {
  150. __block NSException *caught = nil;
  151. [_queue dispatchSync:^{
  152. @try {
  153. [self->_queue verifyIsCurrentQueue];
  154. } @catch (NSException *ex) {
  155. caught = ex;
  156. }
  157. }];
  158. XCTAssertNil(caught);
  159. }
  160. - (void)testEnterCheckedOperationDisallowsNesting {
  161. __block NSException *caught = nil;
  162. __block NSString *problem = nil;
  163. [_queue dispatchSync:^{
  164. @try {
  165. [self->_queue enterCheckedOperation:^{
  166. }];
  167. problem = @"Should not have been able to enter nested enterCheckedOperation";
  168. } @catch (NSException *ex) {
  169. caught = ex;
  170. }
  171. }];
  172. XCTAssertNil(problem);
  173. XCTAssertNotNil(caught);
  174. XCTAssertTrue(
  175. [caught.reason hasPrefix:@"FIRESTORE INTERNAL ASSERTION FAILED: "
  176. @"ExecuteBlocking may not be called before the previous operation "
  177. @"finishes executing"]);
  178. }
  179. /**
  180. * Helper to return a block that adds @(n) to _completedSteps when run and fulfils _expectation if
  181. * the _completedSteps match the _expectedSteps.
  182. */
  183. - (void (^)())blockForStep:(int)n {
  184. return ^void() {
  185. [self->_completedSteps addObject:@(n)];
  186. if (self->_expectedSteps && self->_completedSteps.count >= self->_expectedSteps.count) {
  187. XCTAssertEqualObjects(self->_completedSteps, self->_expectedSteps);
  188. [self->_expectation fulfill];
  189. }
  190. };
  191. }
  192. - (void)testCanScheduleCallbacksInTheFuture {
  193. _expectation = [self expectationWithDescription:@"Expected steps"];
  194. _expectedSteps = @[ @1, @2, @3, @4 ];
  195. [_queue dispatchAsync:[self blockForStep:1]];
  196. [_queue dispatchAsync:^{
  197. [_queue dispatchAfterDelay:0.005 timerID:timerID1 block:[self blockForStep:4]];
  198. [_queue dispatchAfterDelay:0.001 timerID:timerID2 block:[self blockForStep:3]];
  199. }];
  200. [_queue dispatchAsync:[self blockForStep:2]];
  201. [self awaitExpectations];
  202. }
  203. - (void)testCanCancelDelayedCallbacks {
  204. _expectation = [self expectationWithDescription:@"Expected steps"];
  205. _expectedSteps = @[ @1, @3 ];
  206. // Queue everything from the queue to ensure nothing completes before we cancel.
  207. [_queue dispatchAsync:^{
  208. [self->_queue dispatchAsyncAllowingSameQueue:[self blockForStep:1]];
  209. FSTDelayedCallback *step2Timer =
  210. [self->_queue dispatchAfterDelay:.001 timerID:timerID1 block:[self blockForStep:2]];
  211. [self->_queue dispatchAfterDelay:.005 timerID:timerID2 block:[self blockForStep:3]];
  212. XCTAssertTrue([self->_queue containsDelayedCallbackWithTimerID:timerID1]);
  213. [step2Timer cancel];
  214. XCTAssertFalse([self->_queue containsDelayedCallbackWithTimerID:timerID1]);
  215. }];
  216. [self awaitExpectations];
  217. }
  218. - (void)testCanManuallyDrainAllDelayedCallbacksForTesting {
  219. [_queue dispatchAsync:[self blockForStep:1]];
  220. [_queue dispatchAsync:^{
  221. [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:4]];
  222. [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]];
  223. }];
  224. [_queue dispatchAsync:[self blockForStep:2]];
  225. [_queue runDelayedCallbacksUntil:FSTTimerIDAll];
  226. XCTAssertEqualObjects(_completedSteps, (@[ @1, @2, @3, @4 ]));
  227. }
  228. - (void)testCanManuallyDrainSpecificDelayedCallbacksForTesting {
  229. [_queue dispatchAsync:[self blockForStep:1]];
  230. [_queue dispatchAsync:^{
  231. [_queue dispatchAfterDelay:20 timerID:timerID1 block:[self blockForStep:5]];
  232. [_queue dispatchAfterDelay:10 timerID:timerID2 block:[self blockForStep:3]];
  233. [_queue dispatchAfterDelay:15 timerID:timerID3 block:[self blockForStep:4]];
  234. }];
  235. [_queue dispatchAsync:[self blockForStep:2]];
  236. [_queue runDelayedCallbacksUntil:timerID3];
  237. XCTAssertEqualObjects(_completedSteps, (@[ @1, @2, @3, @4 ]));
  238. }
  239. @end