FSTMockDatastore.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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/FSTMockDatastore.h"
  17. #import "Firestore/Source/Auth/FSTEmptyCredentialsProvider.h"
  18. #import "Firestore/Source/Core/FSTSnapshotVersion.h"
  19. #import "Firestore/Source/Local/FSTQueryData.h"
  20. #import "Firestore/Source/Model/FSTMutation.h"
  21. #import "Firestore/Source/Remote/FSTSerializerBeta.h"
  22. #import "Firestore/Source/Remote/FSTStream.h"
  23. #import "Firestore/Source/Util/FSTAssert.h"
  24. #import "Firestore/Source/Util/FSTLogger.h"
  25. #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h"
  26. #include "Firestore/core/src/firebase/firestore/core/database_info.h"
  27. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  28. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  29. using firebase::firestore::core::DatabaseInfo;
  30. using firebase::firestore::model::DatabaseId;
  31. @class GRPCProtoCall;
  32. NS_ASSUME_NONNULL_BEGIN
  33. #pragma mark - FSTMockWatchStream
  34. @interface FSTMockWatchStream : FSTWatchStream
  35. - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore
  36. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  37. credentials:(id<FSTCredentialsProvider>)credentials
  38. serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER;
  39. - (instancetype)initWithDatabase:(const DatabaseInfo *)database
  40. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  41. credentials:(id<FSTCredentialsProvider>)credentials
  42. serializer:(FSTSerializerBeta *)serializer NS_UNAVAILABLE;
  43. - (instancetype)initWithDatabase:(const DatabaseInfo *)database
  44. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  45. credentials:(id<FSTCredentialsProvider>)credentials
  46. responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE;
  47. @property(nonatomic, assign) BOOL open;
  48. @property(nonatomic, strong, readonly) FSTMockDatastore *datastore;
  49. @property(nonatomic, strong, readonly)
  50. NSMutableDictionary<FSTBoxedTargetID *, FSTQueryData *> *activeTargets;
  51. @property(nonatomic, weak, readwrite, nullable) id<FSTWatchStreamDelegate> delegate;
  52. @end
  53. @implementation FSTMockWatchStream
  54. - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore
  55. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  56. credentials:(id<FSTCredentialsProvider>)credentials
  57. serializer:(FSTSerializerBeta *)serializer {
  58. self = [super initWithDatabase:datastore.databaseInfo
  59. workerDispatchQueue:workerDispatchQueue
  60. credentials:credentials
  61. serializer:serializer];
  62. if (self) {
  63. FSTAssert(datastore, @"Datastore must not be nil");
  64. _datastore = datastore;
  65. _activeTargets = [NSMutableDictionary dictionary];
  66. }
  67. return self;
  68. }
  69. #pragma mark - Overridden FSTWatchStream methods.
  70. - (void)startWithDelegate:(id<FSTWatchStreamDelegate>)delegate {
  71. FSTAssert(!self.open, @"Trying to start already started watch stream");
  72. self.open = YES;
  73. self.delegate = delegate;
  74. [self notifyStreamOpen];
  75. }
  76. - (void)stop {
  77. [self.activeTargets removeAllObjects];
  78. self.delegate = nil;
  79. }
  80. - (BOOL)isOpen {
  81. return self.open;
  82. }
  83. - (BOOL)isStarted {
  84. return self.open;
  85. }
  86. - (void)notifyStreamOpen {
  87. [self.delegate watchStreamDidOpen];
  88. }
  89. - (void)notifyStreamInterruptedWithError:(nullable NSError *)error {
  90. [self.delegate watchStreamWasInterruptedWithError:error];
  91. }
  92. - (void)watchQuery:(FSTQueryData *)query {
  93. FSTLog(@"watchQuery: %d: %@", query.targetID, query.query);
  94. self.datastore.watchStreamRequestCount += 1;
  95. // Snapshot version is ignored on the wire
  96. FSTQueryData *sentQueryData =
  97. [query queryDataByReplacingSnapshotVersion:[FSTSnapshotVersion noVersion]
  98. resumeToken:query.resumeToken];
  99. self.activeTargets[@(query.targetID)] = sentQueryData;
  100. }
  101. - (void)unwatchTargetID:(FSTTargetID)targetID {
  102. FSTLog(@"unwatchTargetID: %d", targetID);
  103. [self.activeTargets removeObjectForKey:@(targetID)];
  104. }
  105. - (void)failStreamWithError:(NSError *)error {
  106. self.open = NO;
  107. [self notifyStreamInterruptedWithError:error];
  108. }
  109. #pragma mark - Helper methods.
  110. - (void)writeWatchChange:(FSTWatchChange *)change snapshotVersion:(FSTSnapshotVersion *)snap {
  111. if ([change isKindOfClass:[FSTWatchTargetChange class]]) {
  112. FSTWatchTargetChange *targetChange = (FSTWatchTargetChange *)change;
  113. if (targetChange.cause) {
  114. for (NSNumber *targetID in targetChange.targetIDs) {
  115. if (!self.activeTargets[targetID]) {
  116. // Technically removing an unknown target is valid (e.g. it could race with a
  117. // server-side removal), but we want to pay extra careful attention in tests
  118. // that we only remove targets we listened too.
  119. FSTFail(@"Removing a non-active target");
  120. }
  121. [self.activeTargets removeObjectForKey:targetID];
  122. }
  123. }
  124. }
  125. [self.delegate watchStreamDidChange:change snapshotVersion:snap];
  126. }
  127. @end
  128. #pragma mark - FSTMockWriteStream
  129. @interface FSTWriteStream ()
  130. @property(nonatomic, weak, readwrite, nullable) id<FSTWriteStreamDelegate> delegate;
  131. - (void)notifyStreamOpen;
  132. - (void)notifyStreamInterruptedWithError:(nullable NSError *)error;
  133. @end
  134. @interface FSTMockWriteStream : FSTWriteStream
  135. - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore
  136. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  137. credentials:(id<FSTCredentialsProvider>)credentials
  138. serializer:(FSTSerializerBeta *)serializer NS_DESIGNATED_INITIALIZER;
  139. - (instancetype)initWithDatabase:(const DatabaseInfo *)database
  140. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  141. credentials:(id<FSTCredentialsProvider>)credentials
  142. serializer:(FSTSerializerBeta *)serializer NS_UNAVAILABLE;
  143. - (instancetype)initWithDatabase:(const DatabaseInfo *)database
  144. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  145. credentials:(id<FSTCredentialsProvider>)credentials
  146. responseMessageClass:(Class)responseMessageClass NS_UNAVAILABLE;
  147. @property(nonatomic, strong, readonly) FSTMockDatastore *datastore;
  148. @property(nonatomic, assign) BOOL open;
  149. @property(nonatomic, strong, readonly) NSMutableArray<NSArray<FSTMutation *> *> *sentMutations;
  150. @end
  151. @implementation FSTMockWriteStream
  152. - (instancetype)initWithDatastore:(FSTMockDatastore *)datastore
  153. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue
  154. credentials:(id<FSTCredentialsProvider>)credentials
  155. serializer:(FSTSerializerBeta *)serializer {
  156. self = [super initWithDatabase:datastore.databaseInfo
  157. workerDispatchQueue:workerDispatchQueue
  158. credentials:credentials
  159. serializer:serializer];
  160. if (self) {
  161. FSTAssert(datastore, @"Datastore must not be nil");
  162. _datastore = datastore;
  163. _sentMutations = [NSMutableArray array];
  164. }
  165. return self;
  166. }
  167. #pragma mark - Overridden FSTWriteStream methods.
  168. - (void)startWithDelegate:(id<FSTWriteStreamDelegate>)delegate {
  169. FSTAssert(!self.open, @"Trying to start already started write stream");
  170. self.open = YES;
  171. [self.sentMutations removeAllObjects];
  172. self.delegate = delegate;
  173. [self notifyStreamOpen];
  174. }
  175. - (BOOL)isOpen {
  176. return self.open;
  177. }
  178. - (BOOL)isStarted {
  179. return self.open;
  180. }
  181. - (void)writeHandshake {
  182. self.datastore.writeStreamRequestCount += 1;
  183. self.handshakeComplete = YES;
  184. [self.delegate writeStreamDidCompleteHandshake];
  185. }
  186. - (void)writeMutations:(NSArray<FSTMutation *> *)mutations {
  187. self.datastore.writeStreamRequestCount += 1;
  188. [self.sentMutations addObject:mutations];
  189. }
  190. #pragma mark - Helper methods.
  191. /** Injects a write ack as though it had come from the backend in response to a write. */
  192. - (void)ackWriteWithVersion:(FSTSnapshotVersion *)commitVersion
  193. mutationResults:(NSArray<FSTMutationResult *> *)results {
  194. [self.delegate writeStreamDidReceiveResponseWithVersion:commitVersion mutationResults:results];
  195. }
  196. /** Injects a failed write response as though it had come from the backend. */
  197. - (void)failStreamWithError:(NSError *)error {
  198. self.open = NO;
  199. [self notifyStreamInterruptedWithError:error];
  200. }
  201. /**
  202. * Returns the next write that was "sent to the backend", failing if there are no queued sent
  203. */
  204. - (NSArray<FSTMutation *> *)nextSentWrite {
  205. FSTAssert(self.sentMutations.count > 0,
  206. @"Writes need to happen before you can call nextSentWrite.");
  207. NSArray<FSTMutation *> *result = [self.sentMutations objectAtIndex:0];
  208. [self.sentMutations removeObjectAtIndex:0];
  209. return result;
  210. }
  211. /**
  212. * Returns the number of mutations that have been sent to the backend but not retrieved via
  213. * nextSentWrite yet.
  214. */
  215. - (int)sentMutationsCount {
  216. return (int)self.sentMutations.count;
  217. }
  218. @end
  219. #pragma mark - FSTMockDatastore
  220. @interface FSTMockDatastore ()
  221. @property(nonatomic, strong, nullable) FSTMockWatchStream *watchStream;
  222. @property(nonatomic, strong, nullable) FSTMockWriteStream *writeStream;
  223. /** Properties implemented in FSTDatastore that are nonpublic. */
  224. @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue;
  225. @property(nonatomic, strong, readonly) id<FSTCredentialsProvider> credentials;
  226. @end
  227. @implementation FSTMockDatastore
  228. + (instancetype)mockDatastoreWithWorkerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue {
  229. // This owns the DatabaseInfos since we do not have FirestoreClient instance to own them.
  230. static DatabaseInfo database_info{DatabaseId{"project", "database"}, "persistence", "host",
  231. false};
  232. FSTEmptyCredentialsProvider *credentials = [[FSTEmptyCredentialsProvider alloc] init];
  233. return [[FSTMockDatastore alloc] initWithDatabaseInfo:&database_info
  234. workerDispatchQueue:workerDispatchQueue
  235. credentials:credentials];
  236. }
  237. #pragma mark - Overridden FSTDatastore methods.
  238. - (FSTWatchStream *)createWatchStream {
  239. // FSTAssert(self.databaseInfo, @"DatabaseInfo must not be nil");
  240. self.watchStream = [[FSTMockWatchStream alloc]
  241. initWithDatastore:self
  242. workerDispatchQueue:self.workerDispatchQueue
  243. credentials:self.credentials
  244. serializer:[[FSTSerializerBeta alloc]
  245. initWithDatabaseID:&self.databaseInfo->database_id()]];
  246. return self.watchStream;
  247. }
  248. - (FSTWriteStream *)createWriteStream {
  249. // FSTAssert(self.databaseInfo, @"DatabaseInfo must not be nil");
  250. self.writeStream = [[FSTMockWriteStream alloc]
  251. initWithDatastore:self
  252. workerDispatchQueue:self.workerDispatchQueue
  253. credentials:self.credentials
  254. serializer:[[FSTSerializerBeta alloc]
  255. initWithDatabaseID:&self.databaseInfo->database_id()]];
  256. return self.writeStream;
  257. }
  258. - (void)authorizeAndStartRPC:(GRPCProtoCall *)rpc completion:(FSTVoidErrorBlock)completion {
  259. FSTFail(@"FSTMockDatastore shouldn't be starting any RPCs.");
  260. }
  261. #pragma mark - Method exposed for tests to call.
  262. - (NSArray<FSTMutation *> *)nextSentWrite {
  263. return [self.writeStream nextSentWrite];
  264. }
  265. - (int)writesSent {
  266. return [self.writeStream sentMutationsCount];
  267. }
  268. - (void)ackWriteWithVersion:(FSTSnapshotVersion *)commitVersion
  269. mutationResults:(NSArray<FSTMutationResult *> *)results {
  270. [self.writeStream ackWriteWithVersion:commitVersion mutationResults:results];
  271. }
  272. - (void)failWriteWithError:(NSError *_Nullable)error {
  273. [self.writeStream failStreamWithError:error];
  274. }
  275. - (void)writeWatchTargetAddedWithTargetIDs:(NSArray<FSTBoxedTargetID *> *)targetIDs {
  276. FSTWatchTargetChange *change =
  277. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
  278. targetIDs:targetIDs
  279. cause:nil];
  280. [self writeWatchChange:change snapshotVersion:[FSTSnapshotVersion noVersion]];
  281. }
  282. - (void)writeWatchCurrentWithTargetIDs:(NSArray<FSTBoxedTargetID *> *)targetIDs
  283. snapshotVersion:(FSTSnapshotVersion *)snapshotVersion
  284. resumeToken:(NSData *)resumeToken {
  285. FSTWatchTargetChange *change =
  286. [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
  287. targetIDs:targetIDs
  288. resumeToken:resumeToken];
  289. [self writeWatchChange:change snapshotVersion:snapshotVersion];
  290. }
  291. - (void)writeWatchChange:(FSTWatchChange *)change snapshotVersion:(FSTSnapshotVersion *)snap {
  292. [self.watchStream writeWatchChange:change snapshotVersion:snap];
  293. }
  294. - (void)failWatchStreamWithError:(NSError *)error {
  295. [self.watchStream failStreamWithError:error];
  296. }
  297. - (NSDictionary<FSTBoxedTargetID *, FSTQueryData *> *)activeTargets {
  298. return [self.watchStream.activeTargets copy];
  299. }
  300. - (BOOL)isWatchStreamOpen {
  301. return self.watchStream.isOpen;
  302. }
  303. @end
  304. NS_ASSUME_NONNULL_END