FSTMockDatastore.m 13 KB

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