| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- /*
- * 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 "FirebaseDatabase/Sources/Core/FQuerySpec.h"
- #import "FirebaseDatabase/Sources/Core/Utilities/FPath.h"
- #import "FirebaseDatabase/Sources/FClock.h"
- #import "FirebaseDatabase/Sources/FPathIndex.h"
- #import "FirebaseDatabase/Sources/Persistence/FPruneForest.h"
- #import "FirebaseDatabase/Sources/Persistence/FTrackedQuery.h"
- #import "FirebaseDatabase/Sources/Persistence/FTrackedQueryManager.h"
- #import "FirebaseDatabase/Sources/Snapshot/FSnapshotUtilities.h"
- #import "FirebaseDatabase/Tests/Helpers/FMockStorageEngine.h"
- #import "FirebaseDatabase/Tests/Helpers/FTestCachePolicy.h"
- #import "FirebaseDatabase/Tests/Helpers/FTestClock.h"
- #import "FirebaseDatabase/Tests/Helpers/FTestHelpers.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", (unsigned long)i]))]];
- [manager
- setQueryInactive:[FQuerySpec
- defaultQueryAtPath:PATH(([NSString
- stringWithFormat:@"%lu", (unsigned long)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", (unsigned long)i]))]];
- [manager
- setQueryInactive:[FQuerySpec
- defaultQueryAtPath:PATH(([NSString
- stringWithFormat:@"%lu", (unsigned long)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
|