FSTSyncEngineTestDriver.mm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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. #import <FirebaseFirestore/FIRFirestoreErrors.h>
  18. #include <map>
  19. #include <memory>
  20. #include <string>
  21. #include <unordered_map>
  22. #include <utility>
  23. #include <vector>
  24. #import "Firestore/Source/Core/FSTEventManager.h"
  25. #import "Firestore/Source/Core/FSTQuery.h"
  26. #import "Firestore/Source/Core/FSTSyncEngine.h"
  27. #import "Firestore/Source/Local/FSTLocalStore.h"
  28. #import "Firestore/Source/Local/FSTPersistence.h"
  29. #import "Firestore/Source/Model/FSTMutation.h"
  30. #import "Firestore/Example/Tests/Core/FSTSyncEngine+Testing.h"
  31. #import "Firestore/Example/Tests/SpecTests/FSTMockDatastore.h"
  32. #include "Firestore/core/include/firebase/firestore/firestore_errors.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. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  38. #include "Firestore/core/src/firebase/firestore/objc/objc_compatibility.h"
  39. #include "Firestore/core/src/firebase/firestore/remote/remote_store.h"
  40. #include "Firestore/core/src/firebase/firestore/util/async_queue.h"
  41. #include "Firestore/core/src/firebase/firestore/util/error_apple.h"
  42. #include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h"
  43. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  44. #include "Firestore/core/src/firebase/firestore/util/log.h"
  45. #include "Firestore/core/src/firebase/firestore/util/status.h"
  46. #include "Firestore/core/src/firebase/firestore/util/statusor.h"
  47. #include "Firestore/core/src/firebase/firestore/util/string_format.h"
  48. #include "Firestore/core/src/firebase/firestore/util/to_string.h"
  49. #include "absl/memory/memory.h"
  50. namespace objc = firebase::firestore::objc;
  51. using firebase::firestore::FirestoreErrorCode;
  52. using firebase::firestore::auth::EmptyCredentialsProvider;
  53. using firebase::firestore::auth::HashUser;
  54. using firebase::firestore::auth::User;
  55. using firebase::firestore::core::DatabaseInfo;
  56. using firebase::firestore::core::ListenOptions;
  57. using firebase::firestore::core::QueryListener;
  58. using firebase::firestore::core::ViewSnapshot;
  59. using firebase::firestore::model::DatabaseId;
  60. using firebase::firestore::model::DocumentKey;
  61. using firebase::firestore::model::DocumentKeySet;
  62. using firebase::firestore::model::OnlineState;
  63. using firebase::firestore::model::SnapshotVersion;
  64. using firebase::firestore::model::TargetId;
  65. using firebase::firestore::remote::MockDatastore;
  66. using firebase::firestore::remote::RemoteStore;
  67. using firebase::firestore::remote::WatchChange;
  68. using firebase::firestore::util::AsyncQueue;
  69. using firebase::firestore::util::TimerId;
  70. using firebase::firestore::util::ExecutorLibdispatch;
  71. using firebase::firestore::util::MakeNSError;
  72. using firebase::firestore::util::MakeString;
  73. using firebase::firestore::util::Status;
  74. using firebase::firestore::util::StatusOr;
  75. using firebase::firestore::util::StringFormat;
  76. using firebase::firestore::util::ToString;
  77. using firebase::firestore::util::WrapNSString;
  78. NS_ASSUME_NONNULL_BEGIN
  79. @implementation FSTQueryEvent {
  80. absl::optional<ViewSnapshot> _maybeViewSnapshot;
  81. }
  82. - (const absl::optional<ViewSnapshot> &)viewSnapshot {
  83. return _maybeViewSnapshot;
  84. }
  85. - (void)setViewSnapshot:(absl::optional<ViewSnapshot>)snapshot {
  86. _maybeViewSnapshot = std::move(snapshot);
  87. }
  88. - (NSString *)description {
  89. // The Query is also included in the view, so we skip it.
  90. std::string str = StringFormat("<FSTQueryEvent: viewSnapshot=%s, error=%s>",
  91. ToString(_maybeViewSnapshot), self.error);
  92. return WrapNSString(str);
  93. }
  94. @end
  95. @implementation FSTOutstandingWrite
  96. @end
  97. @interface FSTSyncEngineTestDriver ()
  98. #pragma mark - Parts of the Firestore system that the spec tests need to control.
  99. @property(nonatomic, strong, readonly) FSTEventManager *eventManager;
  100. @property(nonatomic, strong, readonly) FSTLocalStore *localStore;
  101. @property(nonatomic, strong, readonly) FSTSyncEngine *syncEngine;
  102. @property(nonatomic, strong, readonly) id<FSTPersistence> persistence;
  103. #pragma mark - Data structures for holding events sent by the watch stream.
  104. /** A block for the FSTEventAggregator to use to report events to the test. */
  105. @property(nonatomic, strong, readonly) void (^eventHandler)(FSTQueryEvent *);
  106. /** The events received by our eventHandler and not yet retrieved via capturedEventsSinceLastCall */
  107. @property(nonatomic, strong, readonly) NSMutableArray<FSTQueryEvent *> *events;
  108. #pragma mark - Data structures for holding events sent by the write stream.
  109. /** The names of the documents that the client acknowledged during the current spec test step */
  110. @property(nonatomic, strong, readonly) NSMutableArray<NSString *> *acknowledgedDocs;
  111. /** The names of the documents that the client rejected during the current spec test step */
  112. @property(nonatomic, strong, readonly) NSMutableArray<NSString *> *rejectedDocs;
  113. @end
  114. @implementation FSTSyncEngineTestDriver {
  115. std::unique_ptr<AsyncQueue> _workerQueue;
  116. std::unique_ptr<RemoteStore> _remoteStore;
  117. std::unordered_map<TargetId, FSTQueryData *> _expectedActiveTargets;
  118. // ivar is declared as mutable.
  119. std::unordered_map<User, NSMutableArray<FSTOutstandingWrite *> *, HashUser> _outstandingWrites;
  120. DocumentKeySet _expectedLimboDocuments;
  121. /** A dictionary for tracking the listens on queries. */
  122. objc::unordered_map<FSTQuery *, std::shared_ptr<QueryListener>> _queryListeners;
  123. DatabaseInfo _databaseInfo;
  124. User _currentUser;
  125. EmptyCredentialsProvider _credentialProvider;
  126. std::shared_ptr<MockDatastore> _datastore;
  127. }
  128. - (instancetype)initWithPersistence:(id<FSTPersistence>)persistence {
  129. return [self initWithPersistence:persistence
  130. initialUser:User::Unauthenticated()
  131. outstandingWrites:{}];
  132. }
  133. - (instancetype)initWithPersistence:(id<FSTPersistence>)persistence
  134. initialUser:(const User &)initialUser
  135. outstandingWrites:(const FSTOutstandingWriteQueues &)outstandingWrites {
  136. if (self = [super init]) {
  137. // Do a deep copy.
  138. for (const auto &pair : outstandingWrites) {
  139. _outstandingWrites[pair.first] = [pair.second mutableCopy];
  140. }
  141. _events = [NSMutableArray array];
  142. _databaseInfo = {DatabaseId{"project", "database"}, "persistence", "host", false};
  143. // Set up the sync engine and various stores.
  144. dispatch_queue_t queue =
  145. dispatch_queue_create("sync_engine_test_driver", DISPATCH_QUEUE_SERIAL);
  146. _workerQueue = absl::make_unique<AsyncQueue>(absl::make_unique<ExecutorLibdispatch>(queue));
  147. _persistence = persistence;
  148. _localStore = [[FSTLocalStore alloc] initWithPersistence:persistence initialUser:initialUser];
  149. _datastore =
  150. std::make_shared<MockDatastore>(_databaseInfo, _workerQueue.get(), &_credentialProvider);
  151. _remoteStore = absl::make_unique<RemoteStore>(
  152. _localStore, _datastore, _workerQueue.get(), [self](OnlineState onlineState) {
  153. [self.syncEngine applyChangedOnlineState:onlineState];
  154. [self.eventManager applyChangedOnlineState:onlineState];
  155. });
  156. ;
  157. _syncEngine = [[FSTSyncEngine alloc] initWithLocalStore:_localStore
  158. remoteStore:_remoteStore.get()
  159. initialUser:initialUser];
  160. _remoteStore->set_sync_engine(_syncEngine);
  161. _eventManager = [FSTEventManager eventManagerWithSyncEngine:_syncEngine];
  162. // Set up internal event tracking for the spec tests.
  163. NSMutableArray<FSTQueryEvent *> *events = [NSMutableArray array];
  164. _eventHandler = ^(FSTQueryEvent *e) {
  165. [events addObject:e];
  166. };
  167. _events = events;
  168. _currentUser = initialUser;
  169. _acknowledgedDocs = [NSMutableArray array];
  170. _rejectedDocs = [NSMutableArray array];
  171. }
  172. return self;
  173. }
  174. - (const FSTOutstandingWriteQueues &)outstandingWrites {
  175. return _outstandingWrites;
  176. }
  177. - (const DocumentKeySet &)expectedLimboDocuments {
  178. return _expectedLimboDocuments;
  179. }
  180. - (void)setExpectedLimboDocuments:(DocumentKeySet)docs {
  181. _expectedLimboDocuments = std::move(docs);
  182. }
  183. - (void)drainQueue {
  184. _workerQueue->EnqueueBlocking([] {});
  185. }
  186. - (const User &)currentUser {
  187. return _currentUser;
  188. }
  189. - (void)start {
  190. _workerQueue->EnqueueBlocking([&] {
  191. [self.localStore start];
  192. _remoteStore->Start();
  193. });
  194. }
  195. - (void)validateUsage {
  196. // We could relax this if we found a reason to.
  197. HARD_ASSERT(self.events.count == 0, "You must clear all pending events by calling"
  198. " capturedEventsSinceLastCall before calling shutdown.");
  199. }
  200. - (void)shutdown {
  201. _workerQueue->EnqueueBlocking([&] {
  202. _remoteStore->Shutdown();
  203. [self.persistence shutdown];
  204. });
  205. }
  206. - (void)validateNextWriteSent:(FSTMutation *)expectedWrite {
  207. std::vector<FSTMutation *> request = _datastore->NextSentWrite();
  208. // Make sure the write went through the pipe like we expected it to.
  209. HARD_ASSERT(request.size() == 1, "Only single mutation requests are supported at the moment");
  210. FSTMutation *actualWrite = request[0];
  211. HARD_ASSERT([actualWrite isEqual:expectedWrite],
  212. "Mock datastore received write %s but first outstanding mutation was %s", actualWrite,
  213. expectedWrite);
  214. LOG_DEBUG("A write was sent: %s", actualWrite);
  215. }
  216. - (int)sentWritesCount {
  217. return _datastore->WritesSent();
  218. }
  219. - (int)writeStreamRequestCount {
  220. return _datastore->write_stream_request_count();
  221. }
  222. - (int)watchStreamRequestCount {
  223. return _datastore->watch_stream_request_count();
  224. }
  225. - (void)disableNetwork {
  226. _workerQueue->EnqueueBlocking([&] {
  227. // Make sure to execute all writes that are currently queued. This allows us
  228. // to assert on the total number of requests sent before shutdown.
  229. _remoteStore->FillWritePipeline();
  230. _remoteStore->DisableNetwork();
  231. });
  232. }
  233. - (void)enableNetwork {
  234. _workerQueue->EnqueueBlocking([&] { _remoteStore->EnableNetwork(); });
  235. }
  236. - (void)runTimer:(TimerId)timerID {
  237. _workerQueue->RunScheduledOperationsUntil(timerID);
  238. }
  239. - (void)changeUser:(const User &)user {
  240. _currentUser = user;
  241. _workerQueue->EnqueueBlocking([&] { [self.syncEngine credentialDidChangeWithUser:user]; });
  242. }
  243. - (FSTOutstandingWrite *)receiveWriteAckWithVersion:(const SnapshotVersion &)commitVersion
  244. mutationResults:
  245. (std::vector<FSTMutationResult *>)mutationResults {
  246. FSTOutstandingWrite *write = [self currentOutstandingWrites].firstObject;
  247. [[self currentOutstandingWrites] removeObjectAtIndex:0];
  248. [self validateNextWriteSent:write.write];
  249. _workerQueue->EnqueueBlocking(
  250. [&] { _datastore->AckWrite(commitVersion, std::move(mutationResults)); });
  251. return write;
  252. }
  253. - (FSTOutstandingWrite *)receiveWriteError:(int)errorCode
  254. userInfo:(NSDictionary<NSString *, id> *)userInfo
  255. keepInQueue:(BOOL)keepInQueue {
  256. Status error{static_cast<FirestoreErrorCode>(errorCode), MakeString([userInfo description])};
  257. FSTOutstandingWrite *write = [self currentOutstandingWrites].firstObject;
  258. [self validateNextWriteSent:write.write];
  259. // If this is a permanent error, the mutation is not expected to be sent again so we remove it
  260. // from currentOutstandingWrites.
  261. if (!keepInQueue) {
  262. [[self currentOutstandingWrites] removeObjectAtIndex:0];
  263. }
  264. LOG_DEBUG("Failing a write.");
  265. _workerQueue->EnqueueBlocking([&] { _datastore->FailWrite(error); });
  266. return write;
  267. }
  268. - (NSArray<FSTQueryEvent *> *)capturedEventsSinceLastCall {
  269. NSArray<FSTQueryEvent *> *result = [self.events copy];
  270. [self.events removeAllObjects];
  271. return result;
  272. }
  273. - (NSArray<NSString *> *)capturedAcknowledgedWritesSinceLastCall {
  274. NSArray<NSString *> *result = [self.acknowledgedDocs copy];
  275. [self.acknowledgedDocs removeAllObjects];
  276. return result;
  277. }
  278. - (NSArray<NSString *> *)capturedRejectedWritesSinceLastCall {
  279. NSArray<NSString *> *result = [self.rejectedDocs copy];
  280. [self.rejectedDocs removeAllObjects];
  281. return result;
  282. }
  283. - (TargetId)addUserListenerWithQuery:(FSTQuery *)query {
  284. // TODO(dimond): Allow customizing listen options in spec tests
  285. // TODO(dimond): Change spec tests to verify isFromCache on snapshots
  286. ListenOptions options = ListenOptions::FromIncludeMetadataChanges(true);
  287. auto listener = QueryListener::Create(
  288. query, options, [self, query](const StatusOr<ViewSnapshot> &maybe_snapshot) {
  289. FSTQueryEvent *event = [[FSTQueryEvent alloc] init];
  290. event.query = query;
  291. if (maybe_snapshot.ok()) {
  292. [event setViewSnapshot:maybe_snapshot.ValueOrDie()];
  293. } else {
  294. event.error = MakeNSError(maybe_snapshot.status());
  295. }
  296. [self.events addObject:event];
  297. });
  298. _queryListeners[query] = listener;
  299. TargetId targetID;
  300. _workerQueue->EnqueueBlocking([&] { targetID = [self.eventManager addListener:listener]; });
  301. return targetID;
  302. }
  303. - (void)removeUserListenerWithQuery:(FSTQuery *)query {
  304. auto found_iter = _queryListeners.find(query);
  305. if (found_iter != _queryListeners.end()) {
  306. std::shared_ptr<QueryListener> listener = found_iter->second;
  307. _queryListeners.erase(found_iter);
  308. _workerQueue->EnqueueBlocking([&] { [self.eventManager removeListener:listener]; });
  309. }
  310. }
  311. - (void)writeUserMutation:(FSTMutation *)mutation {
  312. FSTOutstandingWrite *write = [[FSTOutstandingWrite alloc] init];
  313. write.write = mutation;
  314. [[self currentOutstandingWrites] addObject:write];
  315. LOG_DEBUG("sending a user write.");
  316. _workerQueue->EnqueueBlocking([=] {
  317. [self.syncEngine writeMutations:{mutation}
  318. completion:^(NSError *_Nullable error) {
  319. LOG_DEBUG("A callback was called with error: %s", error);
  320. write.done = YES;
  321. write.error = error;
  322. NSString *mutationKey =
  323. [NSString stringWithCString:mutation.key.ToString().c_str()
  324. encoding:[NSString defaultCStringEncoding]];
  325. if (error) {
  326. [self.rejectedDocs addObject:mutationKey];
  327. } else {
  328. [self.acknowledgedDocs addObject:mutationKey];
  329. }
  330. }];
  331. });
  332. }
  333. - (void)receiveWatchChange:(const WatchChange &)change
  334. snapshotVersion:(const SnapshotVersion &)snapshot {
  335. _workerQueue->EnqueueBlocking([&] { _datastore->WriteWatchChange(change, snapshot); });
  336. }
  337. - (void)receiveWatchStreamError:(int)errorCode userInfo:(NSDictionary<NSString *, id> *)userInfo {
  338. Status error{static_cast<FirestoreErrorCode>(errorCode), MakeString([userInfo description])};
  339. _workerQueue->EnqueueBlocking([&] {
  340. _datastore->FailWatchStream(error);
  341. // Unlike web, stream should re-open synchronously (if we have any listeners)
  342. if (!_queryListeners.empty()) {
  343. HARD_ASSERT(_datastore->IsWatchStreamOpen(), "Watch stream is open");
  344. }
  345. });
  346. }
  347. - (std::map<DocumentKey, TargetId>)currentLimboDocuments {
  348. return [self.syncEngine currentLimboDocuments];
  349. }
  350. - (const std::unordered_map<TargetId, FSTQueryData *> &)activeTargets {
  351. return _datastore->ActiveTargets();
  352. }
  353. - (const std::unordered_map<TargetId, FSTQueryData *> &)expectedActiveTargets {
  354. return _expectedActiveTargets;
  355. }
  356. - (void)setExpectedActiveTargets:(const std::unordered_map<TargetId, FSTQueryData *> &)targets {
  357. _expectedActiveTargets = targets;
  358. }
  359. #pragma mark - Helper Methods
  360. - (NSMutableArray<FSTOutstandingWrite *> *)currentOutstandingWrites {
  361. NSMutableArray<FSTOutstandingWrite *> *writes = _outstandingWrites[_currentUser];
  362. if (!writes) {
  363. writes = [NSMutableArray array];
  364. _outstandingWrites[_currentUser] = writes;
  365. }
  366. return writes;
  367. }
  368. @end
  369. NS_ASSUME_NONNULL_END