FTrackedQueryManagerTest.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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 <XCTest/XCTest.h>
  17. #import "FTrackedQueryManager.h"
  18. #import "FTrackedQuery.h"
  19. #import "FMockStorageEngine.h"
  20. #import "FPath.h"
  21. #import "FQuerySpec.h"
  22. #import "FPathIndex.h"
  23. #import "FSnapshotUtilities.h"
  24. #import "FClock.h"
  25. #import "FTestClock.h"
  26. #import "FTestHelpers.h"
  27. #import "FPruneForest.h"
  28. #import "FTestCachePolicy.h"
  29. @interface FPruneForest (Test)
  30. - (FImmutableSortedDictionary *)pruneForest;
  31. @end
  32. @interface FTrackedQueryManagerTest : XCTestCase
  33. @end
  34. @implementation FTrackedQueryManagerTest
  35. #define SAMPLE_PARAMS \
  36. ([[[[[FQueryParams defaultInstance] orderBy:[[FPathIndex alloc] initWithPath:PATH(@"child")]] \
  37. startAt:[FSnapshotUtilities nodeFrom:@"startVal"] childKey:@"startKey"] \
  38. endAt:[FSnapshotUtilities nodeFrom:@"endVal"] childKey:@"endKey"] \
  39. limitToLast:5])
  40. #define SAMPLE_QUERY \
  41. ([[FQuerySpec alloc] initWithPath:[FPath pathWithString:@"foo"] params:SAMPLE_PARAMS])
  42. #define DEFAULT_FOO_QUERY \
  43. ([[FQuerySpec alloc] initWithPath:[FPath pathWithString:@"foo"] params:[FQueryParams defaultInstance]])
  44. #define DEFAULT_BAR_QUERY \
  45. ([[FQuerySpec alloc] initWithPath:[FPath pathWithString:@"bar"] params:[FQueryParams defaultInstance]])
  46. - (FTrackedQueryManager *)newManager {
  47. return [self newManagerWithClock:[FSystemClock clock]];
  48. }
  49. - (FTrackedQueryManager *)newManagerWithClock:(id<FClock>)clock {
  50. return [[FTrackedQueryManager alloc] initWithStorageEngine:[[FMockStorageEngine alloc] init]
  51. clock:clock];
  52. }
  53. - (FTrackedQueryManager *)newManagerWithStorageEngine:(id<FStorageEngine>)storageEngine {
  54. return [[FTrackedQueryManager alloc] initWithStorageEngine:storageEngine clock:[FSystemClock clock]];
  55. }
  56. - (void)testFindTrackedQuery {
  57. FTrackedQueryManager *manager = [self newManager];
  58. XCTAssertNil([manager findTrackedQuery:SAMPLE_QUERY]);
  59. [manager setQueryActive:SAMPLE_QUERY];
  60. XCTAssertNotNil([manager findTrackedQuery:SAMPLE_QUERY]);
  61. }
  62. - (void)testRemoveTrackedQuery {
  63. FTrackedQueryManager *manager = [self newManager];
  64. [manager setQueryActive:SAMPLE_QUERY];
  65. XCTAssertNotNil([manager findTrackedQuery:SAMPLE_QUERY]);
  66. [manager removeTrackedQuery:SAMPLE_QUERY];
  67. XCTAssertNil([manager findTrackedQuery:SAMPLE_QUERY]);
  68. [manager verifyCache];
  69. }
  70. - (void)testSetQueryActiveAndInactive {
  71. FTestClock *clock = [[FTestClock alloc] init];
  72. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  73. [manager setQueryActive:SAMPLE_QUERY];
  74. FTrackedQuery *q = [manager findTrackedQuery:SAMPLE_QUERY];
  75. XCTAssertTrue(q.isActive);
  76. XCTAssertEqual(q.lastUse, clock.currentTime);
  77. [manager verifyCache];
  78. [clock tick];
  79. [manager setQueryInactive:SAMPLE_QUERY];
  80. q = [manager findTrackedQuery:SAMPLE_QUERY];
  81. XCTAssertFalse(q.isActive);
  82. XCTAssertEqual(q.lastUse, clock.currentTime);
  83. [manager verifyCache];
  84. }
  85. - (void)testSetQueryComplete {
  86. FTrackedQueryManager *manager = [self newManager];
  87. [manager setQueryActive:SAMPLE_QUERY];
  88. [manager setQueryComplete:SAMPLE_QUERY];
  89. XCTAssertTrue([manager findTrackedQuery:SAMPLE_QUERY].isComplete);
  90. [manager verifyCache];
  91. }
  92. - (void)testSetQueriesComplete {
  93. FTrackedQueryManager *manager = [self newManager];
  94. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  95. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/bar")]];
  96. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"elsewhere")]];
  97. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo") params:SAMPLE_PARAMS]];
  98. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/baz") params:SAMPLE_PARAMS]];
  99. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"elsewhere") params:SAMPLE_PARAMS]];
  100. [manager setQueriesCompleteAtPath:PATH(@"foo")];
  101. XCTAssertTrue([manager findTrackedQuery:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]].isComplete);
  102. XCTAssertTrue([manager findTrackedQuery:[FQuerySpec defaultQueryAtPath:PATH(@"foo/bar")]].isComplete);
  103. XCTAssertTrue([manager findTrackedQuery:[[FQuerySpec alloc] initWithPath:PATH(@"foo") params:SAMPLE_PARAMS]].isComplete);
  104. XCTAssertTrue([manager findTrackedQuery:[[FQuerySpec alloc] initWithPath:PATH(@"foo/baz") params:SAMPLE_PARAMS]].isComplete);
  105. XCTAssertFalse([manager findTrackedQuery:[FQuerySpec defaultQueryAtPath:PATH(@"elsewhere")]].isComplete);
  106. XCTAssertFalse([manager findTrackedQuery:[[FQuerySpec alloc] initWithPath:PATH(@"elsewhere") params:SAMPLE_PARAMS]].isComplete);
  107. [manager verifyCache];
  108. }
  109. - (void)testIsQueryComplete {
  110. FTrackedQueryManager *manager = [self newManager];
  111. [manager setQueryActive:SAMPLE_QUERY];
  112. [manager setQueryComplete:SAMPLE_QUERY];
  113. [manager setQueryActive:DEFAULT_BAR_QUERY];
  114. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"baz")]];
  115. [manager setQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"baz")]];
  116. XCTAssertTrue([manager isQueryComplete:SAMPLE_QUERY]);
  117. XCTAssertFalse([manager isQueryComplete:DEFAULT_BAR_QUERY]);
  118. XCTAssertFalse([manager isQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"")]]);
  119. XCTAssertTrue([manager isQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"baz")]]);
  120. XCTAssertTrue([manager isQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"baz/quu")]]);
  121. }
  122. - (void)testPruneOldQueries {
  123. FTestClock *clock = [[FTestClock alloc] init];
  124. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  125. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"active1")]];
  126. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"active2")]];
  127. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"pinned1")]];
  128. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"pinned2")]];
  129. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive1")]];
  130. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive1")]];
  131. [clock tick];
  132. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive2")]];
  133. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive2")]];
  134. [clock tick];
  135. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive3")]];
  136. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive3")]];
  137. [clock tick];
  138. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive4")]];
  139. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive4")]];
  140. [clock tick];
  141. // Should remove the first two inactive queries
  142. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.5 maxQueries:NSUIntegerMax]];
  143. [self checkPruneForest:forest
  144. pathsToKeep:@[@"active1", @"active2", @"pinned1", @"pinned2", @"inactive3", @"inactive4"]
  145. pathsToPrune:@[@"inactive1", @"inactive2"]];
  146. // Should remove the other two inactive queries
  147. forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1 maxQueries:NSUIntegerMax]];
  148. [self checkPruneForest:forest
  149. pathsToKeep:@[@"active1", @"active2", @"pinned1", @"pinned2"]
  150. pathsToPrune:@[@"inactive3", @"inactive4"]];
  151. // Nothing left to prune
  152. forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1 maxQueries:NSUIntegerMax]];
  153. XCTAssertFalse([forest prunesAnything]);
  154. [manager verifyCache];
  155. }
  156. - (void) testPruneQueriesOverMaxSize {
  157. FTestClock *clock = [[FTestClock alloc] init];
  158. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  159. for (NSUInteger i = 0; i < 10; i++) {
  160. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
  161. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
  162. [clock tick];
  163. }
  164. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.2 maxQueries:6]];
  165. [self checkPruneForest:forest
  166. pathsToKeep:@[@"4", @"5", @"6", @"7", @"8", @"9"]
  167. pathsToPrune:@[@"0", @"1", @"2", @"3"]];
  168. }
  169. - (void) testPruneDefaultWithDeeperQueries {
  170. FTestClock *clock = [[FTestClock alloc] init];
  171. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  172. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  173. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
  174. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
  175. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  176. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1.0 maxQueries:NSUIntegerMax]];
  177. [self checkPruneForest:forest pathsToKeep:@[@"foo/a", @"foo/b"] pathsToPrune:@[@"foo"]];
  178. [manager verifyCache];
  179. }
  180. - (void) testPruneQueriesWithDefaultQueryOnParent {
  181. FTestClock *clock = [[FTestClock alloc] init];
  182. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  183. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  184. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
  185. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
  186. [manager setQueryInactive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
  187. [manager setQueryInactive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
  188. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1.0 maxQueries:NSUIntegerMax]];
  189. [self checkPruneForest:forest pathsToKeep:@[@"foo"] pathsToPrune:@[]];
  190. [manager verifyCache];
  191. }
  192. - (void) testPruneQueriesOverMaxSizeUsingPercent {
  193. FTestClock *clock = [[FTestClock alloc] init];
  194. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  195. for (NSUInteger i = 0; i < 10; i++) {
  196. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
  197. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
  198. [clock tick];
  199. }
  200. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.6 maxQueries:6]];
  201. [self checkPruneForest:forest
  202. pathsToKeep:@[@"6", @"7", @"8", @"9"]
  203. pathsToPrune:@[@"0", @"1", @"2", @"3", @"4", @"5"]];
  204. }
  205. - (void)checkPruneForest:(FPruneForest *)pruneForest pathsToKeep:(NSArray *)toKeep pathsToPrune:(NSArray *)toPrune {
  206. FPruneForest *checkForest = [FPruneForest empty];
  207. for (NSString *path in toPrune) {
  208. checkForest = [checkForest prunePath:PATH(path)];
  209. }
  210. for (NSString *path in toKeep) {
  211. checkForest = [checkForest keepPath:PATH(path)];
  212. }
  213. XCTAssertEqualObjects([pruneForest pruneForest], [checkForest pruneForest]);
  214. }
  215. - (void)testKnownCompleteChildren {
  216. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  217. FTrackedQueryManager *manager = [self newManagerWithStorageEngine:engine];
  218. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo")], [NSSet set]);
  219. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/a")]];
  220. [manager setQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"foo/a")]];
  221. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/not-included")]];
  222. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/deep/not-included")]];
  223. [manager setQueryActive:SAMPLE_QUERY];
  224. FTrackedQuery *query = [manager findTrackedQuery:SAMPLE_QUERY];
  225. [engine setTrackedQueryKeys:[NSSet setWithArray:@[@"d", @"e"]] forQueryId:query.queryId];
  226. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo")], ([NSSet setWithArray:@[@"a", @"d", @"e"]]));
  227. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"")], [NSSet set]);
  228. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo/baz")], [NSSet set]);
  229. }
  230. - (void)testEnsureTrackedQueryForNewQuery {
  231. FTestClock *clock = [[FTestClock alloc] init];
  232. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  233. [manager ensureCompleteTrackedQueryAtPath:PATH(@"foo")];
  234. FTrackedQuery *query = [manager findTrackedQuery:DEFAULT_FOO_QUERY];
  235. XCTAssertTrue(query.isComplete);
  236. XCTAssertEqual(query.lastUse, clock.currentTime);
  237. }
  238. - (void)testEnsureTrackedQueryForAlreadyTrackedQuery {
  239. FTestClock *clock = [[FTestClock alloc] init];
  240. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  241. [manager setQueryActive:DEFAULT_FOO_QUERY];
  242. NSTimeInterval lastTick = clock.currentTime;
  243. [clock tick];
  244. [manager ensureCompleteTrackedQueryAtPath:PATH(@"foo")];
  245. XCTAssertEqual([manager findTrackedQuery:DEFAULT_FOO_QUERY].lastUse, lastTick);
  246. }
  247. - (void)testHasActiveDefaultQuery {
  248. FTrackedQueryManager *manager = [self newManager];
  249. [manager setQueryActive:SAMPLE_QUERY];
  250. [manager setQueryActive:DEFAULT_BAR_QUERY];
  251. XCTAssertFalse([manager hasActiveDefaultQueryAtPath:PATH(@"foo")]);
  252. XCTAssertFalse([manager hasActiveDefaultQueryAtPath:PATH(@"")]);
  253. XCTAssertTrue([manager hasActiveDefaultQueryAtPath:PATH(@"bar")]);
  254. XCTAssertTrue([manager hasActiveDefaultQueryAtPath:PATH(@"bar/baz")]);
  255. }
  256. - (void)testCacheSanity {
  257. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  258. FTrackedQueryManager *manager = [self newManagerWithStorageEngine:engine];
  259. [manager setQueryActive:SAMPLE_QUERY];
  260. [manager setQueryActive:DEFAULT_FOO_QUERY];
  261. [manager verifyCache];
  262. [manager setQueryComplete:SAMPLE_QUERY];
  263. [manager verifyCache];
  264. [manager setQueryInactive:DEFAULT_FOO_QUERY];
  265. [manager verifyCache];
  266. FTrackedQueryManager *manager2 = [self newManagerWithStorageEngine:engine];
  267. XCTAssertNotNil([manager2 findTrackedQuery:SAMPLE_QUERY]);
  268. XCTAssertNotNil([manager2 findTrackedQuery:DEFAULT_FOO_QUERY]);
  269. [manager2 verifyCache];
  270. }
  271. @end