FTrackedQueryManagerTest.m 16 KB

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