FTrackedQueryManagerTest.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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:
  161. PATH(([NSString stringWithFormat:@"%lu", (unsigned long)i]))]];
  162. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:
  163. PATH(([NSString stringWithFormat:@"%lu", (unsigned long)i]))]];
  164. [clock tick];
  165. }
  166. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.2 maxQueries:6]];
  167. [self checkPruneForest:forest
  168. pathsToKeep:@[@"4", @"5", @"6", @"7", @"8", @"9"]
  169. pathsToPrune:@[@"0", @"1", @"2", @"3"]];
  170. }
  171. - (void) testPruneDefaultWithDeeperQueries {
  172. FTestClock *clock = [[FTestClock alloc] init];
  173. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  174. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  175. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
  176. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
  177. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  178. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1.0 maxQueries:NSUIntegerMax]];
  179. [self checkPruneForest:forest pathsToKeep:@[@"foo/a", @"foo/b"] pathsToPrune:@[@"foo"]];
  180. [manager verifyCache];
  181. }
  182. - (void) testPruneQueriesWithDefaultQueryOnParent {
  183. FTestClock *clock = [[FTestClock alloc] init];
  184. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  185. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
  186. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
  187. [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
  188. [manager setQueryInactive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
  189. [manager setQueryInactive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
  190. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1.0 maxQueries:NSUIntegerMax]];
  191. [self checkPruneForest:forest pathsToKeep:@[@"foo"] pathsToPrune:@[]];
  192. [manager verifyCache];
  193. }
  194. - (void) testPruneQueriesOverMaxSizeUsingPercent {
  195. FTestClock *clock = [[FTestClock alloc] init];
  196. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  197. for (NSUInteger i = 0; i < 10; i++) {
  198. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:
  199. PATH(([NSString stringWithFormat:@"%lu", (unsigned long)i]))]];
  200. [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:
  201. PATH(([NSString stringWithFormat:@"%lu", (unsigned long)i]))]];
  202. [clock tick];
  203. }
  204. FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.6 maxQueries:6]];
  205. [self checkPruneForest:forest
  206. pathsToKeep:@[@"6", @"7", @"8", @"9"]
  207. pathsToPrune:@[@"0", @"1", @"2", @"3", @"4", @"5"]];
  208. }
  209. - (void)checkPruneForest:(FPruneForest *)pruneForest pathsToKeep:(NSArray *)toKeep pathsToPrune:(NSArray *)toPrune {
  210. FPruneForest *checkForest = [FPruneForest empty];
  211. for (NSString *path in toPrune) {
  212. checkForest = [checkForest prunePath:PATH(path)];
  213. }
  214. for (NSString *path in toKeep) {
  215. checkForest = [checkForest keepPath:PATH(path)];
  216. }
  217. XCTAssertEqualObjects([pruneForest pruneForest], [checkForest pruneForest]);
  218. }
  219. - (void)testKnownCompleteChildren {
  220. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  221. FTrackedQueryManager *manager = [self newManagerWithStorageEngine:engine];
  222. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo")], [NSSet set]);
  223. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/a")]];
  224. [manager setQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"foo/a")]];
  225. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/not-included")]];
  226. [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/deep/not-included")]];
  227. [manager setQueryActive:SAMPLE_QUERY];
  228. FTrackedQuery *query = [manager findTrackedQuery:SAMPLE_QUERY];
  229. [engine setTrackedQueryKeys:[NSSet setWithArray:@[@"d", @"e"]] forQueryId:query.queryId];
  230. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo")], ([NSSet setWithArray:@[@"a", @"d", @"e"]]));
  231. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"")], [NSSet set]);
  232. XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo/baz")], [NSSet set]);
  233. }
  234. - (void)testEnsureTrackedQueryForNewQuery {
  235. FTestClock *clock = [[FTestClock alloc] init];
  236. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  237. [manager ensureCompleteTrackedQueryAtPath:PATH(@"foo")];
  238. FTrackedQuery *query = [manager findTrackedQuery:DEFAULT_FOO_QUERY];
  239. XCTAssertTrue(query.isComplete);
  240. XCTAssertEqual(query.lastUse, clock.currentTime);
  241. }
  242. - (void)testEnsureTrackedQueryForAlreadyTrackedQuery {
  243. FTestClock *clock = [[FTestClock alloc] init];
  244. FTrackedQueryManager *manager = [self newManagerWithClock:clock];
  245. [manager setQueryActive:DEFAULT_FOO_QUERY];
  246. NSTimeInterval lastTick = clock.currentTime;
  247. [clock tick];
  248. [manager ensureCompleteTrackedQueryAtPath:PATH(@"foo")];
  249. XCTAssertEqual([manager findTrackedQuery:DEFAULT_FOO_QUERY].lastUse, lastTick);
  250. }
  251. - (void)testHasActiveDefaultQuery {
  252. FTrackedQueryManager *manager = [self newManager];
  253. [manager setQueryActive:SAMPLE_QUERY];
  254. [manager setQueryActive:DEFAULT_BAR_QUERY];
  255. XCTAssertFalse([manager hasActiveDefaultQueryAtPath:PATH(@"foo")]);
  256. XCTAssertFalse([manager hasActiveDefaultQueryAtPath:PATH(@"")]);
  257. XCTAssertTrue([manager hasActiveDefaultQueryAtPath:PATH(@"bar")]);
  258. XCTAssertTrue([manager hasActiveDefaultQueryAtPath:PATH(@"bar/baz")]);
  259. }
  260. - (void)testCacheSanity {
  261. FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
  262. FTrackedQueryManager *manager = [self newManagerWithStorageEngine:engine];
  263. [manager setQueryActive:SAMPLE_QUERY];
  264. [manager setQueryActive:DEFAULT_FOO_QUERY];
  265. [manager verifyCache];
  266. [manager setQueryComplete:SAMPLE_QUERY];
  267. [manager verifyCache];
  268. [manager setQueryInactive:DEFAULT_FOO_QUERY];
  269. [manager verifyCache];
  270. FTrackedQueryManager *manager2 = [self newManagerWithStorageEngine:engine];
  271. XCTAssertNotNil([manager2 findTrackedQuery:SAMPLE_QUERY]);
  272. XCTAssertNotNil([manager2 findTrackedQuery:DEFAULT_FOO_QUERY]);
  273. [manager2 verifyCache];
  274. }
  275. @end