|
18 | 18 |
|
19 | 19 | #include <memory>
|
20 | 20 | #include <string>
|
| 21 | +#include <vector> |
21 | 22 |
|
22 | 23 | #import "Firestore/Protos/objc/firestore/local/Target.pbobjc.h"
|
23 | 24 | #import "Firestore/Source/Local/FSTLevelDB.h"
|
24 | 25 | #import "Firestore/Source/Local/FSTLevelDBKey.h"
|
25 | 26 | #import "Firestore/Source/Local/FSTLevelDBMigrations.h"
|
| 27 | +#import "Firestore/Source/Local/FSTLevelDBMutationQueue.h" |
26 | 28 | #import "Firestore/Source/Local/FSTLevelDBQueryCache.h"
|
27 | 29 |
|
28 | 30 | #include "Firestore/core/src/firebase/firestore/util/ordered_code.h"
|
| 31 | +#include "Firestore/core/src/firebase/firestore/util/status.h" |
| 32 | +#include "Firestore/core/test/firebase/firestore/testutil/testutil.h" |
| 33 | +#include "absl/strings/match.h" |
29 | 34 | #include "leveldb/db.h"
|
30 | 35 |
|
31 | 36 | #import "Firestore/Example/Tests/Local/FSTPersistenceTestHelpers.h"
|
32 | 37 |
|
33 | 38 | NS_ASSUME_NONNULL_BEGIN
|
34 | 39 |
|
| 40 | +using firebase::firestore::FirestoreErrorCode; |
35 | 41 | using firebase::firestore::local::LevelDbTransaction;
|
36 | 42 | using firebase::firestore::util::OrderedCode;
|
| 43 | +using firebase::firestore::testutil::Key; |
37 | 44 | using leveldb::DB;
|
38 | 45 | using leveldb::Options;
|
39 | 46 | using leveldb::Status;
|
@@ -64,54 +71,136 @@ - (void)tearDown {
|
64 | 71 | - (void)testAddsTargetGlobal {
|
65 | 72 | FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.get()];
|
66 | 73 | XCTAssertNil(metadata, @"Not expecting metadata yet, we should have an empty db");
|
67 |
| - LevelDbTransaction transaction(_db.get(), "testAddsTargetGlobal"); |
68 |
| - [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; |
69 |
| - transaction.Commit(); |
| 74 | + [FSTLevelDBMigrations runMigrationsWithDatabase:_db.get()]; |
| 75 | + |
70 | 76 | metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.get()];
|
71 | 77 | XCTAssertNotNil(metadata, @"Migrations should have added the metadata");
|
72 | 78 | }
|
73 | 79 |
|
74 | 80 | - (void)testSetsVersionNumber {
|
75 |
| - LevelDbTransaction transaction(_db.get(), "testSetsVersionNumber"); |
76 |
| - FSTLevelDBSchemaVersion initial = |
77 |
| - [FSTLevelDBMigrations schemaVersionWithTransaction:&transaction]; |
78 |
| - XCTAssertEqual(0, initial, "No version should be equivalent to 0"); |
79 |
| - |
80 |
| - // Pick an arbitrary high migration number and migrate to it. |
81 |
| - [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; |
82 |
| - FSTLevelDBSchemaVersion actual = [FSTLevelDBMigrations schemaVersionWithTransaction:&transaction]; |
83 |
| - XCTAssertGreaterThan(actual, 0, @"Expected to migrate to a schema version > 0"); |
| 81 | + { |
| 82 | + LevelDbTransaction transaction(_db.get(), "testSetsVersionNumber before"); |
| 83 | + FSTLevelDBSchemaVersion initial = |
| 84 | + [FSTLevelDBMigrations schemaVersionWithTransaction:&transaction]; |
| 85 | + XCTAssertEqual(0, initial, "No version should be equivalent to 0"); |
| 86 | + } |
| 87 | + |
| 88 | + { |
| 89 | + // Pick an arbitrary high migration number and migrate to it. |
| 90 | + [FSTLevelDBMigrations runMigrationsWithDatabase:_db.get()]; |
| 91 | + |
| 92 | + LevelDbTransaction transaction(_db.get(), "testSetsVersionNumber after"); |
| 93 | + FSTLevelDBSchemaVersion actual = |
| 94 | + [FSTLevelDBMigrations schemaVersionWithTransaction:&transaction]; |
| 95 | + XCTAssertGreaterThan(actual, 0, @"Expected to migrate to a schema version > 0"); |
| 96 | + } |
84 | 97 | }
|
85 | 98 |
|
86 |
| -- (void)testCountsQueries { |
87 |
| - NSUInteger expected = 50; |
| 99 | +#define ASSERT_NOT_FOUND(transaction, key) \ |
| 100 | + do { \ |
| 101 | + std::string unused_result; \ |
| 102 | + Status status = transaction.Get(key, &unused_result); \ |
| 103 | + XCTAssertTrue(status.IsNotFound()); \ |
| 104 | + } while (0) |
| 105 | + |
| 106 | +#define ASSERT_FOUND(transaction, key) \ |
| 107 | + do { \ |
| 108 | + std::string unused_result; \ |
| 109 | + Status status = transaction.Get(key, &unused_result); \ |
| 110 | + XCTAssertTrue(status.ok()); \ |
| 111 | + } while (0) |
| 112 | + |
| 113 | +- (void)testDropsTheQueryCache { |
| 114 | + NSString *userID = @"user"; |
| 115 | + FSTBatchID batchID = 1; |
| 116 | + FSTTargetID targetID = 2; |
| 117 | + |
| 118 | + FSTDocumentKey *key1 = Key("documents/1"); |
| 119 | + FSTDocumentKey *key2 = Key("documents/2"); |
| 120 | + |
| 121 | + std::string targetKeys[] = { |
| 122 | + [FSTLevelDBTargetKey keyWithTargetID:targetID], |
| 123 | + [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key1], |
| 124 | + [FSTLevelDBTargetDocumentKey keyWithTargetID:targetID documentKey:key2], |
| 125 | + [FSTLevelDBDocumentTargetKey keyWithDocumentKey:key1 targetID:targetID], |
| 126 | + [FSTLevelDBDocumentTargetKey keyWithDocumentKey:key2 targetID:targetID], |
| 127 | + [FSTLevelDBQueryTargetKey keyWithCanonicalID:"foo.bar.baz" targetID:targetID], |
| 128 | + }; |
| 129 | + |
| 130 | + // Keys that should not be modified by the dropping the query cache |
| 131 | + std::string preservedKeys[] = { |
| 132 | + [self dummyKeyForTable:"targetA"], |
| 133 | + [FSTLevelDBMutationQueueKey keyWithUserID:userID], |
| 134 | + [FSTLevelDBMutationKey keyWithUserID:userID batchID:batchID], |
| 135 | + }; |
| 136 | + |
| 137 | + [FSTLevelDBMigrations runMigrationsWithDatabase:_db.get() upToVersion:2]; |
88 | 138 | {
|
89 | 139 | // Setup some targets to be counted in the migration.
|
90 |
| - LevelDbTransaction transaction(_db.get(), "testCountsQueries setup"); |
91 |
| - for (int i = 0; i < expected; i++) { |
92 |
| - std::string key = [FSTLevelDBTargetKey keyWithTargetID:i]; |
93 |
| - transaction.Put(key, "dummy"); |
| 140 | + LevelDbTransaction transaction(_db.get(), "testDropsTheQueryCache setup"); |
| 141 | + for (const std::string &key : targetKeys) { |
| 142 | + transaction.Put(key, "target"); |
| 143 | + } |
| 144 | + for (const std::string &key : preservedKeys) { |
| 145 | + transaction.Put(key, "preserved"); |
94 | 146 | }
|
95 |
| - // Add a dummy entry after the targets to make sure the iteration is correctly bounded. |
96 |
| - // Use a table that would sort logically right after that table 'target'. |
97 |
| - std::string dummyKey; |
98 |
| - // Magic number that indicates a table name follows. Needed to mimic the prefix to the target |
99 |
| - // table. |
100 |
| - OrderedCode::WriteSignedNumIncreasing(&dummyKey, 5); |
101 |
| - OrderedCode::WriteString(&dummyKey, "targetA"); |
102 |
| - transaction.Put(dummyKey, "dummy"); |
103 | 147 | transaction.Commit();
|
104 | 148 | }
|
105 | 149 |
|
| 150 | + [FSTLevelDBMigrations runMigrationsWithDatabase:_db.get() upToVersion:3]; |
106 | 151 | {
|
107 |
| - LevelDbTransaction transaction(_db.get(), "testCountsQueries"); |
108 |
| - [FSTLevelDBMigrations runMigrationsWithTransaction:&transaction]; |
109 |
| - transaction.Commit(); |
| 152 | + LevelDbTransaction transaction(_db.get(), "testDropsTheQueryCache"); |
| 153 | + for (const std::string &key : targetKeys) { |
| 154 | + ASSERT_NOT_FOUND(transaction, key); |
| 155 | + } |
| 156 | + for (const std::string &key : preservedKeys) { |
| 157 | + ASSERT_FOUND(transaction, key); |
| 158 | + } |
| 159 | + |
110 | 160 | FSTPBTargetGlobal *metadata = [FSTLevelDBQueryCache readTargetMetadataFromDB:_db.get()];
|
111 |
| - XCTAssertEqual(expected, metadata.targetCount, @"Failed to count all of the targets we added"); |
| 161 | + XCTAssertNotNil(metadata, @"Metadata should have been added"); |
| 162 | + XCTAssertEqual(metadata.targetCount, 0); |
112 | 163 | }
|
113 | 164 | }
|
114 | 165 |
|
| 166 | +- (void)testDropsTheQueryCacheWithThousandsOfEntries { |
| 167 | + [FSTLevelDBMigrations runMigrationsWithDatabase:_db.get() upToVersion:2]; |
| 168 | + { |
| 169 | + // Setup some targets to be destroyed. |
| 170 | + LevelDbTransaction transaction(_db.get(), "testDropsTheQueryCacheWithThousandsOfEntries setup"); |
| 171 | + for (int i = 0; i < 10000; ++i) { |
| 172 | + transaction.Put([FSTLevelDBTargetKey keyWithTargetID:i], ""); |
| 173 | + } |
| 174 | + transaction.Commit(); |
| 175 | + } |
| 176 | + |
| 177 | + [FSTLevelDBMigrations runMigrationsWithDatabase:_db.get() upToVersion:3]; |
| 178 | + { |
| 179 | + LevelDbTransaction transaction(_db.get(), "Verify"); |
| 180 | + std::string prefix = [FSTLevelDBTargetKey keyPrefix]; |
| 181 | + |
| 182 | + auto it = transaction.NewIterator(); |
| 183 | + std::vector<std::string> found_keys; |
| 184 | + for (it->Seek(prefix); it->Valid() && absl::StartsWith(it->key(), prefix); it->Next()) { |
| 185 | + found_keys.push_back(std::string{it->key()}); |
| 186 | + } |
| 187 | + |
| 188 | + XCTAssertEqual(found_keys, std::vector<std::string>{}); |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +/** |
| 193 | + * Creates the name of a dummy entry to make sure the iteration is correctly bounded. |
| 194 | + */ |
| 195 | +- (std::string)dummyKeyForTable:(const char *)tableName { |
| 196 | + std::string dummyKey; |
| 197 | + // Magic number that indicates a table name follows. Needed to mimic the prefix to the target |
| 198 | + // table. |
| 199 | + OrderedCode::WriteSignedNumIncreasing(&dummyKey, 5); |
| 200 | + OrderedCode::WriteString(&dummyKey, tableName); |
| 201 | + return dummyKey; |
| 202 | +} |
| 203 | + |
115 | 204 | @end
|
116 | 205 |
|
117 | 206 | NS_ASSUME_NONNULL_END
|
0 commit comments