FSTSyncEngineTestDriver.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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 "Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h"
  17. #include <unordered_map>
  18. #import <FirebaseFirestore/FIRFirestoreErrors.h>
  19. #import <GRPCClient/GRPCCall.h>
  20. #import "Firestore/Source/Core/FSTEventManager.h"
  21. #import "Firestore/Source/Core/FSTQuery.h"
  22. #import "Firestore/Source/Core/FSTSnapshotVersion.h"
  23. #import "Firestore/Source/Core/FSTSyncEngine.h"
  24. #import "Firestore/Source/Local/FSTLocalStore.h"
  25. #import "Firestore/Source/Local/FSTPersistence.h"
  26. #import "Firestore/Source/Model/FSTMutation.h"
  27. #import "Firestore/Source/Remote/FSTDatastore.h"
  28. #import "Firestore/Source/Remote/FSTWatchChange.h"
  29. #import "Firestore/Source/Util/FSTAssert.h"
  30. #import "Firestore/Source/Util/FSTLogger.h"
  31. #import "Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h"
  32. #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h"
  33. #include "Firestore/core/src/firebase/firestore/auth/empty_credentials_provider.h"
  34. #include "Firestore/core/src/firebase/firestore/auth/user.h"
  35. #include "Firestore/core/src/firebase/firestore/core/database_info.h"
  36. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  37. using firebase::firestore::auth::EmptyCredentialsProvider;
  38. using firebase::firestore::auth::HashUser;
  39. using firebase::firestore::auth::User;
  40. using firebase::firestore::core::DatabaseInfo;
  41. using firebase::firestore::model::DatabaseId;
  42. NS_ASSUME_NONNULL_BEGIN
  43. @implementation FSTQueryEvent
  44. - (NSString *)description {
  45. // The Query is also included in the view, so we skip it.
  46. return [NSString stringWithFormat:@"<FSTQueryEvent: viewSnapshot=%@, error=%@>",
  47. self.viewSnapshot, self.error];
  48. }
  49. @end
  50. @implementation FSTOutstandingWrite
  51. @end
  52. @interface FSTSyncEngineTestDriver ()
  53. #pragma mark - Parts of the Firestore system that the spec tests need to control.
  54. @property(nonatomic, strong, readonly) FSTMockDatastore *datastore;
  55. @property(nonatomic, strong, readonly) FSTEventManager *eventManager;
  56. @property(nonatomic, strong, readonly) FSTRemoteStore *remoteStore;
  57. @property(nonatomic, strong, readonly) FSTLocalStore *localStore;
  58. @property(nonatomic, strong, readonly) FSTSyncEngine *syncEngine;
  59. @property(nonatomic, strong, readonly) FSTDispatchQueue *dispatchQueue;
  60. #pragma mark - Data structures for holding events sent by the watch stream.
  61. /** A block for the FSTEventAggregator to use to report events to the test. */
  62. @property(nonatomic, strong, readonly) void (^eventHandler)(FSTQueryEvent *);
  63. /** The events received by our eventHandler and not yet retrieved via capturedEventsSinceLastCall */
  64. @property(nonatomic, strong, readonly) NSMutableArray<FSTQueryEvent *> *events;
  65. /** A dictionary for tracking the listens on queries. */
  66. @property(nonatomic, strong, readonly)
  67. NSMutableDictionary<FSTQuery *, FSTQueryListener *> *queryListeners;
  68. @end
  69. @implementation FSTSyncEngineTestDriver {
  70. // ivar is declared as mutable.
  71. std::unordered_map<User, NSMutableArray<FSTOutstandingWrite *> *, HashUser> _outstandingWrites;
  72. DatabaseInfo _databaseInfo;
  73. User _currentUser;
  74. EmptyCredentialsProvider _credentialProvider;
  75. }
  76. - (instancetype)initWithPersistence:(id<FSTPersistence>)persistence
  77. garbageCollector:(id<FSTGarbageCollector>)garbageCollector {
  78. return [self initWithPersistence:persistence
  79. garbageCollector:garbageCollector
  80. initialUser:User::Unauthenticated()
  81. outstandingWrites:{}];
  82. }
  83. - (instancetype)initWithPersistence:(id<FSTPersistence>)persistence
  84. garbageCollector:(id<FSTGarbageCollector>)garbageCollector
  85. initialUser:(const User &)initialUser
  86. outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites {
  87. if (self = [super init]) {
  88. // Do a deep copy.
  89. for (const auto &pair : outstandingWrites) {
  90. _outstandingWrites[pair.first] = [pair.second mutableCopy];
  91. }
  92. _events = [NSMutableArray array];
  93. _databaseInfo = {DatabaseId{"project", "database"}, "persistence", "host", false};
  94. // Set up the sync engine and various stores.
  95. dispatch_queue_t queue =
  96. dispatch_queue_create("sync_engine_test_driver", DISPATCH_QUEUE_SERIAL);
  97. _dispatchQueue = [FSTDispatchQueue queueWith:queue];
  98. _localStore = [[FSTLocalStore alloc] initWithPersistence:persistence
  99. garbageCollector:garbageCollector
  100. initialUser:initialUser];
  101. _datastore = [[FSTMockDatastore alloc] initWithDatabaseInfo:&_databaseInfo
  102. workerDispatchQueue:_dispatchQueue
  103. credentials:&_credentialProvider];
  104. _remoteStore = [[FSTRemoteStore alloc] initWithLocalStore:_localStore
  105. datastore:_datastore
  106. workerDispatchQueue:_dispatchQueue];
  107. _syncEngine = [[FSTSyncEngine alloc] initWithLocalStore:_localStore
  108. remoteStore:_remoteStore
  109. initialUser:initialUser];
  110. _remoteStore.syncEngine = _syncEngine;
  111. _eventManager = [FSTEventManager eventManagerWithSyncEngine:_syncEngine];
  112. _remoteStore.onlineStateDelegate = self;
  113. // Set up internal event tracking for the spec tests.
  114. NSMutableArray<FSTQueryEvent *> *events = [NSMutableArray array];
  115. _eventHandler = ^(FSTQueryEvent *e) {
  116. [events addObject:e];
  117. };
  118. _events = events;
  119. _queryListeners = [NSMutableDictionary dictionary];
  120. _expectedLimboDocuments = [NSSet set];
  121. _expectedActiveTargets = [NSDictionary dictionary];
  122. _currentUser = initialUser;
  123. }
  124. return self;
  125. }
  126. - (const FSTOutstandingWriteQueues &)outstandingWrites {
  127. return _outstandingWrites;
  128. }
  129. - (const User &)currentUser {
  130. return _currentUser;
  131. }
  132. - (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
  133. [self.syncEngine applyChangedOnlineState:onlineState];
  134. [self.eventManager applyChangedOnlineState:onlineState];
  135. }
  136. - (void)start {
  137. [self.dispatchQueue dispatchSync:^{
  138. [self.localStore start];
  139. [self.remoteStore start];
  140. }];
  141. }
  142. - (void)validateUsage {
  143. // We could relax this if we found a reason to.
  144. FSTAssert(self.events.count == 0,
  145. @"You must clear all pending events by calling"
  146. " capturedEventsSinceLastCall before calling shutdown.");
  147. }
  148. - (void)shutdown {
  149. [self.dispatchQueue dispatchSync:^{
  150. [self.remoteStore shutdown];
  151. [self.localStore shutdown];
  152. }];
  153. }
  154. - (void)validateNextWriteSent:(FSTMutation *)expectedWrite {
  155. NSArray<FSTMutation *> *request = [self.datastore nextSentWrite];
  156. // Make sure the write went through the pipe like we expected it to.
  157. FSTAssert(request.count == 1, @"Only single mutation requests are supported at the moment");
  158. FSTMutation *actualWrite = request[0];
  159. FSTAssert([actualWrite isEqual:expectedWrite],
  160. @"Mock datastore received write %@ but first outstanding mutation was %@", actualWrite,
  161. expectedWrite);
  162. FSTLog(@"A write was sent: %@", actualWrite);
  163. }
  164. - (int)sentWritesCount {
  165. return [self.datastore writesSent];
  166. }
  167. - (int)writeStreamRequestCount {
  168. return [self.datastore writeStreamRequestCount];
  169. }
  170. - (int)watchStreamRequestCount {
  171. return [self.datastore watchStreamRequestCount];
  172. }
  173. - (void)disableNetwork {
  174. [self.dispatchQueue dispatchSync:^{
  175. // Make sure to execute all writes that are currently queued. This allows us
  176. // to assert on the total number of requests sent before shutdown.
  177. [self.remoteStore fillWritePipeline];
  178. [self.remoteStore disableNetwork];
  179. }];
  180. }
  181. - (void)enableNetwork {
  182. [self.dispatchQueue dispatchSync:^{
  183. [self.remoteStore enableNetwork];
  184. }];
  185. }
  186. - (void)runTimer:(FSTTimerID)timerID {
  187. [self.dispatchQueue runDelayedCallbacksUntil:timerID];
  188. }
  189. - (void)changeUser:(const User &)user {
  190. _currentUser = user;
  191. [self.dispatchQueue dispatchSync:^{
  192. [self.syncEngine userDidChange:user];
  193. }];
  194. }
  195. - (FSTOutstandingWrite *)receiveWriteAckWithVersion:(FSTSnapshotVersion *)commitVersion
  196. mutationResults:
  197. (NSArray<FSTMutationResult *> *)mutationResults {
  198. FSTOutstandingWrite *write = [self currentOutstandingWrites].firstObject;
  199. [[self currentOutstandingWrites] removeObjectAtIndex:0];
  200. [self validateNextWriteSent:write.write];
  201. [self.dispatchQueue dispatchSync:^{
  202. [self.datastore ackWriteWithVersion:commitVersion mutationResults:mutationResults];
  203. }];
  204. return write;
  205. }
  206. - (FSTOutstandingWrite *)receiveWriteError:(int)errorCode
  207. userInfo:(NSDictionary<NSString *, id> *)userInfo {
  208. NSError *error =
  209. [NSError errorWithDomain:FIRFirestoreErrorDomain code:errorCode userInfo:userInfo];
  210. FSTOutstandingWrite *write = [self currentOutstandingWrites].firstObject;
  211. [self validateNextWriteSent:write.write];
  212. // If this is a permanent error, the mutation is not expected to be sent again so we remove it
  213. // from currentOutstandingWrites.
  214. if ([FSTDatastore isPermanentWriteError:error]) {
  215. [[self currentOutstandingWrites] removeObjectAtIndex:0];
  216. }
  217. FSTLog(@"Failing a write.");
  218. [self.dispatchQueue dispatchSync:^{
  219. [self.datastore failWriteWithError:error];
  220. }];
  221. return write;
  222. }
  223. - (NSArray<FSTQueryEvent *> *)capturedEventsSinceLastCall {
  224. NSArray<FSTQueryEvent *> *result = [self.events copy];
  225. [self.events removeAllObjects];
  226. return result;
  227. }
  228. - (FSTTargetID)addUserListenerWithQuery:(FSTQuery *)query {
  229. // TODO(dimond): Allow customizing listen options in spec tests
  230. // TODO(dimond): Change spec tests to verify isFromCache on snapshots
  231. FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES
  232. includeDocumentMetadataChanges:YES
  233. waitForSyncWhenOnline:NO];
  234. FSTQueryListener *listener = [[FSTQueryListener alloc]
  235. initWithQuery:query
  236. options:options
  237. viewSnapshotHandler:^(FSTViewSnapshot *_Nullable snapshot, NSError *_Nullable error) {
  238. FSTQueryEvent *event = [[FSTQueryEvent alloc] init];
  239. event.query = query;
  240. event.viewSnapshot = snapshot;
  241. event.error = error;
  242. [self.events addObject:event];
  243. }];
  244. self.queryListeners[query] = listener;
  245. __block FSTTargetID targetID;
  246. [self.dispatchQueue dispatchSync:^{
  247. targetID = [self.eventManager addListener:listener];
  248. }];
  249. return targetID;
  250. }
  251. - (void)removeUserListenerWithQuery:(FSTQuery *)query {
  252. FSTQueryListener *listener = self.queryListeners[query];
  253. [self.queryListeners removeObjectForKey:query];
  254. [self.dispatchQueue dispatchSync:^{
  255. [self.eventManager removeListener:listener];
  256. }];
  257. }
  258. - (void)writeUserMutation:(FSTMutation *)mutation {
  259. FSTOutstandingWrite *write = [[FSTOutstandingWrite alloc] init];
  260. write.write = mutation;
  261. [[self currentOutstandingWrites] addObject:write];
  262. FSTLog(@"sending a user write.");
  263. [self.dispatchQueue dispatchSync:^{
  264. [self.syncEngine writeMutations:@[ mutation ]
  265. completion:^(NSError *_Nullable error) {
  266. FSTLog(@"A callback was called with error: %@", error);
  267. write.done = YES;
  268. write.error = error;
  269. }];
  270. }];
  271. }
  272. - (void)receiveWatchChange:(FSTWatchChange *)change
  273. snapshotVersion:(FSTSnapshotVersion *_Nullable)snapshot {
  274. [self.dispatchQueue dispatchSync:^{
  275. [self.datastore writeWatchChange:change snapshotVersion:snapshot];
  276. }];
  277. }
  278. - (void)receiveWatchStreamError:(int)errorCode userInfo:(NSDictionary<NSString *, id> *)userInfo {
  279. NSError *error =
  280. [NSError errorWithDomain:FIRFirestoreErrorDomain code:errorCode userInfo:userInfo];
  281. [self.dispatchQueue dispatchSync:^{
  282. [self.datastore failWatchStreamWithError:error];
  283. // Unlike web, stream should re-open synchronously (if we have any listeners)
  284. if (self.queryListeners.count > 0) {
  285. FSTAssert(self.datastore.isWatchStreamOpen, @"Watch stream is open");
  286. }
  287. }];
  288. }
  289. - (NSDictionary<FSTDocumentKey *, FSTBoxedTargetID *> *)currentLimboDocuments {
  290. return [self.syncEngine currentLimboDocuments];
  291. }
  292. - (NSDictionary<FSTBoxedTargetID *, FSTQueryData *> *)activeTargets {
  293. return [[self.datastore activeTargets] copy];
  294. }
  295. #pragma mark - Helper Methods
  296. - (NSMutableArray<FSTOutstandingWrite *> *)currentOutstandingWrites {
  297. NSMutableArray<FSTOutstandingWrite *> *writes = _outstandingWrites[_currentUser];
  298. if (!writes) {
  299. writes = [NSMutableArray array];
  300. _outstandingWrites[_currentUser] = writes;
  301. }
  302. return writes;
  303. }
  304. @end
  305. NS_ASSUME_NONNULL_END