| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- /*
- * Copyright 2017 Google
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #import <XCTest/XCTest.h>
- #import "FTrackedQueryManager.h"
- #import "FTrackedQuery.h"
- #import "FMockStorageEngine.h"
- #import "FPath.h"
- #import "FQuerySpec.h"
- #import "FPathIndex.h"
- #import "FSnapshotUtilities.h"
- #import "FClock.h"
- #import "FTestClock.h"
- #import "FTestHelpers.h"
- #import "FPruneForest.h"
- #import "FTestCachePolicy.h"
- @interface FPruneForest (Test)
- - (FImmutableSortedDictionary *)pruneForest;
- @end
- @interface FTrackedQueryManagerTest : XCTestCase
- @end
- @implementation FTrackedQueryManagerTest
- #define SAMPLE_PARAMS \
- ([[[[[FQueryParams defaultInstance] orderBy:[[FPathIndex alloc] initWithPath:PATH(@"child")]] \
- startAt:[FSnapshotUtilities nodeFrom:@"startVal"] childKey:@"startKey"] \
- endAt:[FSnapshotUtilities nodeFrom:@"endVal"] childKey:@"endKey"] \
- limitToLast:5])
- #define SAMPLE_QUERY \
- ([[FQuerySpec alloc] initWithPath:[FPath pathWithString:@"foo"] params:SAMPLE_PARAMS])
- #define DEFAULT_FOO_QUERY \
- ([[FQuerySpec alloc] initWithPath:[FPath pathWithString:@"foo"] params:[FQueryParams defaultInstance]])
- #define DEFAULT_BAR_QUERY \
- ([[FQuerySpec alloc] initWithPath:[FPath pathWithString:@"bar"] params:[FQueryParams defaultInstance]])
- - (FTrackedQueryManager *)newManager {
- return [self newManagerWithClock:[FSystemClock clock]];
- }
- - (FTrackedQueryManager *)newManagerWithClock:(id<FClock>)clock {
- return [[FTrackedQueryManager alloc] initWithStorageEngine:[[FMockStorageEngine alloc] init]
- clock:clock];
- }
- - (FTrackedQueryManager *)newManagerWithStorageEngine:(id<FStorageEngine>)storageEngine {
- return [[FTrackedQueryManager alloc] initWithStorageEngine:storageEngine clock:[FSystemClock clock]];
- }
- - (void)testFindTrackedQuery {
- FTrackedQueryManager *manager = [self newManager];
- XCTAssertNil([manager findTrackedQuery:SAMPLE_QUERY]);
- [manager setQueryActive:SAMPLE_QUERY];
- XCTAssertNotNil([manager findTrackedQuery:SAMPLE_QUERY]);
- }
- - (void)testRemoveTrackedQuery {
- FTrackedQueryManager *manager = [self newManager];
- [manager setQueryActive:SAMPLE_QUERY];
- XCTAssertNotNil([manager findTrackedQuery:SAMPLE_QUERY]);
- [manager removeTrackedQuery:SAMPLE_QUERY];
- XCTAssertNil([manager findTrackedQuery:SAMPLE_QUERY]);
- [manager verifyCache];
- }
- - (void)testSetQueryActiveAndInactive {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- [manager setQueryActive:SAMPLE_QUERY];
- FTrackedQuery *q = [manager findTrackedQuery:SAMPLE_QUERY];
- XCTAssertTrue(q.isActive);
- XCTAssertEqual(q.lastUse, clock.currentTime);
- [manager verifyCache];
- [clock tick];
- [manager setQueryInactive:SAMPLE_QUERY];
- q = [manager findTrackedQuery:SAMPLE_QUERY];
- XCTAssertFalse(q.isActive);
- XCTAssertEqual(q.lastUse, clock.currentTime);
- [manager verifyCache];
- }
- - (void)testSetQueryComplete {
- FTrackedQueryManager *manager = [self newManager];
- [manager setQueryActive:SAMPLE_QUERY];
- [manager setQueryComplete:SAMPLE_QUERY];
- XCTAssertTrue([manager findTrackedQuery:SAMPLE_QUERY].isComplete);
- [manager verifyCache];
- }
- - (void)testSetQueriesComplete {
- FTrackedQueryManager *manager = [self newManager];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/bar")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"elsewhere")]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo") params:SAMPLE_PARAMS]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/baz") params:SAMPLE_PARAMS]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"elsewhere") params:SAMPLE_PARAMS]];
- [manager setQueriesCompleteAtPath:PATH(@"foo")];
- XCTAssertTrue([manager findTrackedQuery:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]].isComplete);
- XCTAssertTrue([manager findTrackedQuery:[FQuerySpec defaultQueryAtPath:PATH(@"foo/bar")]].isComplete);
- XCTAssertTrue([manager findTrackedQuery:[[FQuerySpec alloc] initWithPath:PATH(@"foo") params:SAMPLE_PARAMS]].isComplete);
- XCTAssertTrue([manager findTrackedQuery:[[FQuerySpec alloc] initWithPath:PATH(@"foo/baz") params:SAMPLE_PARAMS]].isComplete);
- XCTAssertFalse([manager findTrackedQuery:[FQuerySpec defaultQueryAtPath:PATH(@"elsewhere")]].isComplete);
- XCTAssertFalse([manager findTrackedQuery:[[FQuerySpec alloc] initWithPath:PATH(@"elsewhere") params:SAMPLE_PARAMS]].isComplete);
- [manager verifyCache];
- }
- - (void)testIsQueryComplete {
- FTrackedQueryManager *manager = [self newManager];
- [manager setQueryActive:SAMPLE_QUERY];
- [manager setQueryComplete:SAMPLE_QUERY];
- [manager setQueryActive:DEFAULT_BAR_QUERY];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"baz")]];
- [manager setQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"baz")]];
- XCTAssertTrue([manager isQueryComplete:SAMPLE_QUERY]);
- XCTAssertFalse([manager isQueryComplete:DEFAULT_BAR_QUERY]);
- XCTAssertFalse([manager isQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"")]]);
- XCTAssertTrue([manager isQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"baz")]]);
- XCTAssertTrue([manager isQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"baz/quu")]]);
- }
- - (void)testPruneOldQueries {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"active1")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"active2")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"pinned1")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"pinned2")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive1")]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive1")]];
- [clock tick];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive2")]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive2")]];
- [clock tick];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive3")]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive3")]];
- [clock tick];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive4")]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"inactive4")]];
- [clock tick];
- // Should remove the first two inactive queries
- FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.5 maxQueries:NSUIntegerMax]];
- [self checkPruneForest:forest
- pathsToKeep:@[@"active1", @"active2", @"pinned1", @"pinned2", @"inactive3", @"inactive4"]
- pathsToPrune:@[@"inactive1", @"inactive2"]];
- // Should remove the other two inactive queries
- forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1 maxQueries:NSUIntegerMax]];
- [self checkPruneForest:forest
- pathsToKeep:@[@"active1", @"active2", @"pinned1", @"pinned2"]
- pathsToPrune:@[@"inactive3", @"inactive4"]];
- // Nothing left to prune
- forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1 maxQueries:NSUIntegerMax]];
- XCTAssertFalse([forest prunesAnything]);
- [manager verifyCache];
- }
- - (void) testPruneQueriesOverMaxSize {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- for (NSUInteger i = 0; i < 10; i++) {
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
- [clock tick];
- }
- FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.2 maxQueries:6]];
- [self checkPruneForest:forest
- pathsToKeep:@[@"4", @"5", @"6", @"7", @"8", @"9"]
- pathsToPrune:@[@"0", @"1", @"2", @"3"]];
- }
- - (void) testPruneDefaultWithDeeperQueries {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
- FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1.0 maxQueries:NSUIntegerMax]];
- [self checkPruneForest:forest pathsToKeep:@[@"foo/a", @"foo/b"] pathsToPrune:@[@"foo"]];
- [manager verifyCache];
- }
- - (void) testPruneQueriesWithDefaultQueryOnParent {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo")]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
- [manager setQueryActive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
- [manager setQueryInactive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/a") params:SAMPLE_PARAMS]];
- [manager setQueryInactive:[[FQuerySpec alloc] initWithPath:PATH(@"foo/b") params:SAMPLE_PARAMS]];
- FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:1.0 maxQueries:NSUIntegerMax]];
- [self checkPruneForest:forest pathsToKeep:@[@"foo"] pathsToPrune:@[]];
- [manager verifyCache];
- }
- - (void) testPruneQueriesOverMaxSizeUsingPercent {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- for (NSUInteger i = 0; i < 10; i++) {
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
- [manager setQueryInactive:[FQuerySpec defaultQueryAtPath:PATH(([NSString stringWithFormat:@"%lu", i]))]];
- [clock tick];
- }
- FPruneForest *forest = [manager pruneOldQueries:[[FTestCachePolicy alloc] initWithPercent:0.6 maxQueries:6]];
- [self checkPruneForest:forest
- pathsToKeep:@[@"6", @"7", @"8", @"9"]
- pathsToPrune:@[@"0", @"1", @"2", @"3", @"4", @"5"]];
- }
- - (void)checkPruneForest:(FPruneForest *)pruneForest pathsToKeep:(NSArray *)toKeep pathsToPrune:(NSArray *)toPrune {
- FPruneForest *checkForest = [FPruneForest empty];
- for (NSString *path in toPrune) {
- checkForest = [checkForest prunePath:PATH(path)];
- }
- for (NSString *path in toKeep) {
- checkForest = [checkForest keepPath:PATH(path)];
- }
- XCTAssertEqualObjects([pruneForest pruneForest], [checkForest pruneForest]);
- }
- - (void)testKnownCompleteChildren {
- FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithStorageEngine:engine];
- XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo")], [NSSet set]);
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/a")]];
- [manager setQueryComplete:[FQuerySpec defaultQueryAtPath:PATH(@"foo/a")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/not-included")]];
- [manager setQueryActive:[FQuerySpec defaultQueryAtPath:PATH(@"foo/deep/not-included")]];
- [manager setQueryActive:SAMPLE_QUERY];
- FTrackedQuery *query = [manager findTrackedQuery:SAMPLE_QUERY];
- [engine setTrackedQueryKeys:[NSSet setWithArray:@[@"d", @"e"]] forQueryId:query.queryId];
- XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo")], ([NSSet setWithArray:@[@"a", @"d", @"e"]]));
- XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"")], [NSSet set]);
- XCTAssertEqualObjects([manager knownCompleteChildrenAtPath:PATH(@"foo/baz")], [NSSet set]);
- }
- - (void)testEnsureTrackedQueryForNewQuery {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- [manager ensureCompleteTrackedQueryAtPath:PATH(@"foo")];
- FTrackedQuery *query = [manager findTrackedQuery:DEFAULT_FOO_QUERY];
- XCTAssertTrue(query.isComplete);
- XCTAssertEqual(query.lastUse, clock.currentTime);
- }
- - (void)testEnsureTrackedQueryForAlreadyTrackedQuery {
- FTestClock *clock = [[FTestClock alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithClock:clock];
- [manager setQueryActive:DEFAULT_FOO_QUERY];
- NSTimeInterval lastTick = clock.currentTime;
- [clock tick];
- [manager ensureCompleteTrackedQueryAtPath:PATH(@"foo")];
- XCTAssertEqual([manager findTrackedQuery:DEFAULT_FOO_QUERY].lastUse, lastTick);
- }
- - (void)testHasActiveDefaultQuery {
- FTrackedQueryManager *manager = [self newManager];
- [manager setQueryActive:SAMPLE_QUERY];
- [manager setQueryActive:DEFAULT_BAR_QUERY];
- XCTAssertFalse([manager hasActiveDefaultQueryAtPath:PATH(@"foo")]);
- XCTAssertFalse([manager hasActiveDefaultQueryAtPath:PATH(@"")]);
- XCTAssertTrue([manager hasActiveDefaultQueryAtPath:PATH(@"bar")]);
- XCTAssertTrue([manager hasActiveDefaultQueryAtPath:PATH(@"bar/baz")]);
- }
- - (void)testCacheSanity {
- FMockStorageEngine *engine = [[FMockStorageEngine alloc] init];
- FTrackedQueryManager *manager = [self newManagerWithStorageEngine:engine];
- [manager setQueryActive:SAMPLE_QUERY];
- [manager setQueryActive:DEFAULT_FOO_QUERY];
- [manager verifyCache];
- [manager setQueryComplete:SAMPLE_QUERY];
- [manager verifyCache];
- [manager setQueryInactive:DEFAULT_FOO_QUERY];
- [manager verifyCache];
- FTrackedQueryManager *manager2 = [self newManagerWithStorageEngine:engine];
- XCTAssertNotNil([manager2 findTrackedQuery:SAMPLE_QUERY]);
- XCTAssertNotNil([manager2 findTrackedQuery:DEFAULT_FOO_QUERY]);
- [manager2 verifyCache];
- }
- @end
|