1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/indexed_db/indexed_db_tombstone_sweeper.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/test/metrics/histogram_tester.h"
15 #include "base/test/task_environment.h"
16 #include "base/time/tick_clock.h"
17 #include "components/services/storage/indexed_db/leveldb/leveldb_factory.h"
18 #include "components/services/storage/indexed_db/leveldb/mock_level_db.h"
19 #include "components/services/storage/indexed_db/scopes/leveldb_scopes.h"
20 #include "components/services/storage/indexed_db/scopes/varint_coding.h"
21 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_database.h"
22 #include "components/services/storage/indexed_db/transactional_leveldb/transactional_leveldb_factory.h"
23 #include "content/browser/indexed_db/indexed_db_class_factory.h"
24 #include "content/browser/indexed_db/indexed_db_leveldb_env.h"
25 #include "content/browser/indexed_db/indexed_db_leveldb_operations.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
29 #include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
30 #include "third_party/blink/public/common/indexeddb/indexeddb_metadata.h"
31 #include "third_party/leveldatabase/env_chromium.h"
32 #include "third_party/leveldatabase/src/include/leveldb/db.h"
33 #include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
34 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
35
36 using blink::IndexedDBDatabaseMetadata;
37 using blink::IndexedDBIndexMetadata;
38 using blink::IndexedDBKey;
39 using blink::IndexedDBKeyPath;
40 using blink::IndexedDBObjectStoreMetadata;
41
42 namespace content {
43 class BrowserContext;
44
45 namespace indexed_db_tombstone_sweeper_unittest {
46 using ::testing::_;
47 using ::testing::Eq;
48 using ::testing::Return;
49 using ::testing::StrictMock;
50 using Status = ::leveldb::Status;
51 using Slice = ::leveldb::Slice;
52
53 constexpr int kRoundIterations = 11;
54 constexpr int kMaxIterations = 100;
55 const base::TimeTicks kTaskStartTime =
56 base::TimeTicks() + base::TimeDelta::FromSeconds(1);
57 const base::TimeTicks kTaskEndTime =
58 base::TimeTicks() + base::TimeDelta::FromSeconds(2);
59
60 constexpr int64_t kDb1 = 1;
61 constexpr int64_t kDb2 = 1;
62 constexpr int64_t kOs1 = 3;
63 constexpr int64_t kOs2 = 5;
64 constexpr int64_t kOs3 = 8;
65 constexpr int64_t kOs4 = 9;
66 constexpr int64_t kIndex1 = 31;
67 constexpr int64_t kIndex2 = 32;
68 constexpr int64_t kIndex3 = 35;
69 constexpr int kTombstoneSize = 33;
70
71 MATCHER_P(SliceEq,
72 str,
73 std::string(negation ? "isn't" : "is") + " equal to " +
74 base::HexEncode(str.data(), str.size())) {
75 *result_listener << "which is " << base::HexEncode(arg.data(), arg.size());
76 return std::string(arg.data(), arg.size()) == str;
77 }
78
79 class MockTickClock : public base::TickClock {
80 public:
MockTickClock()81 MockTickClock() {}
~MockTickClock()82 ~MockTickClock() override {}
83
84 MOCK_CONST_METHOD0(NowTicks, base::TimeTicks());
85 };
86
87 class IndexedDBTombstoneSweeperTest : public testing::Test {
88 public:
IndexedDBTombstoneSweeperTest()89 IndexedDBTombstoneSweeperTest() {}
~IndexedDBTombstoneSweeperTest()90 ~IndexedDBTombstoneSweeperTest() {}
91
PopulateMultiDBMetdata()92 void PopulateMultiDBMetdata() {
93 // db1
94 // os1
95 // os2
96 // index1
97 // index2
98 metadata_.emplace_back(base::ASCIIToUTF16("db1"), kDb1, 1, 29);
99 auto& db1 = metadata_.back();
100 db1.object_stores[kOs1] = IndexedDBObjectStoreMetadata(
101 base::ASCIIToUTF16("os1"), kOs1, IndexedDBKeyPath(), false, 1000);
102 db1.object_stores[kOs2] = IndexedDBObjectStoreMetadata(
103 base::ASCIIToUTF16("os2"), kOs2, IndexedDBKeyPath(), false, 1000);
104 auto& os2 = db1.object_stores[kOs2];
105 os2.indexes[kIndex1] = IndexedDBIndexMetadata(
106 base::ASCIIToUTF16("index1"), kIndex1, IndexedDBKeyPath(), true, false);
107 os2.indexes[kIndex2] = IndexedDBIndexMetadata(
108 base::ASCIIToUTF16("index2"), kIndex2, IndexedDBKeyPath(), true, false);
109 // db2
110 // os3
111 // index3
112 // os4
113 metadata_.emplace_back(base::ASCIIToUTF16("db2"), kDb2, 1, 29);
114 auto& db2 = metadata_.back();
115 db2.object_stores[kOs3] = IndexedDBObjectStoreMetadata(
116 base::ASCIIToUTF16("os3"), kOs3, IndexedDBKeyPath(), false, 1000);
117 db2.object_stores[kOs4] = IndexedDBObjectStoreMetadata(
118 base::ASCIIToUTF16("os4"), kOs4, IndexedDBKeyPath(), false, 1000);
119 auto& os3 = db2.object_stores[kOs3];
120 os3.indexes[kIndex3] = IndexedDBIndexMetadata(
121 base::ASCIIToUTF16("index3"), kIndex3, IndexedDBKeyPath(), true, false);
122 }
123
PopulateSingleIndexDBMetadata()124 void PopulateSingleIndexDBMetadata() {
125 // db1
126 // os1
127 // index1
128 metadata_.emplace_back(base::ASCIIToUTF16("db1"), kDb1, 1, 29);
129 auto& db1 = metadata_.back();
130 db1.object_stores[kOs1] = IndexedDBObjectStoreMetadata(
131 base::ASCIIToUTF16("os1"), kOs1, IndexedDBKeyPath(), false, 1000);
132 auto& os2 = db1.object_stores[kOs1];
133 os2.indexes[kIndex1] = IndexedDBIndexMetadata(
134 base::ASCIIToUTF16("index1"), kIndex1, IndexedDBKeyPath(), true, false);
135 }
136
SetupMockDB()137 void SetupMockDB() {
138 sweeper_ = std::make_unique<IndexedDBTombstoneSweeper>(
139 kRoundIterations, kMaxIterations, &mock_db_);
140 sweeper_->SetStartSeedsForTesting(0, 0, 0);
141 }
142
SetupRealDB()143 void SetupRealDB() {
144 scoped_refptr<LevelDBState> level_db_state;
145 leveldb::Status s;
146 std::tie(level_db_state, s, std::ignore) =
147 IndexedDBClassFactory::Get()->leveldb_factory().OpenLevelDBState(
148 base::FilePath(), indexed_db::GetDefaultLevelDBComparator(),
149 /* create_if_missing=*/true);
150 ASSERT_TRUE(s.ok());
151 in_memory_db_ =
152 IndexedDBClassFactory::Get()
153 ->transactional_leveldb_factory()
154 .CreateLevelDBDatabase(std::move(level_db_state), nullptr, nullptr,
155 TransactionalLevelDBDatabase::
156 kDefaultMaxOpenIteratorsPerDatabase);
157 sweeper_ = std::make_unique<IndexedDBTombstoneSweeper>(
158 kRoundIterations, kMaxIterations, in_memory_db_->db());
159 sweeper_->SetStartSeedsForTesting(0, 0, 0);
160 }
161
SetClockExpectations()162 void SetClockExpectations() {
163 EXPECT_CALL(tick_clock_, NowTicks())
164 .WillOnce(testing::Return(kTaskStartTime))
165 .WillOnce(testing::Return(kTaskEndTime));
166 sweeper_->SetClockForTesting(&tick_clock_);
167 }
168
SetFirstClockExpectation()169 void SetFirstClockExpectation() {
170 EXPECT_CALL(tick_clock_, NowTicks())
171 .WillOnce(testing::Return(kTaskStartTime));
172 sweeper_->SetClockForTesting(&tick_clock_);
173 }
174
ExpectUmaTombstones(int num,int size,bool reached_max=false)175 void ExpectUmaTombstones(int num, int size, bool reached_max = false) {
176 std::string category = reached_max ? "MaxIterations" : "Complete";
177 histogram_tester_.ExpectUniqueSample(
178 "WebCore.IndexedDB.TombstoneSweeper.NumDeletedTombstones." + category,
179 num, 1);
180 histogram_tester_.ExpectUniqueSample(
181 "WebCore.IndexedDB.TombstoneSweeper.DeletedTombstonesSize." + category,
182 size, 1);
183 }
184
ExpectTaskTimeRecorded()185 void ExpectTaskTimeRecorded() {
186 histogram_tester_.ExpectTimeBucketCount(
187 "WebCore.IndexedDB.TombstoneSweeper.DeletionTotalTime.Complete",
188 base::TimeDelta::FromSeconds(1), 1);
189 }
190
ExpectIndexEntry(leveldb::MockIterator & iterator,int64_t db,int64_t os,int64_t index,const IndexedDBKey & index_key,const IndexedDBKey & primary_key,int index_version)191 void ExpectIndexEntry(leveldb::MockIterator& iterator,
192 int64_t db,
193 int64_t os,
194 int64_t index,
195 const IndexedDBKey& index_key,
196 const IndexedDBKey& primary_key,
197 int index_version) {
198 testing::InSequence sequence_enforcer;
199
200 EXPECT_CALL(iterator, key())
201 .WillOnce(Return(
202 IndexDataKey::Encode(db, os, index, index_key, primary_key)));
203 std::string value_str;
204 EncodeVarInt(index_version, &value_str);
205 EncodeIDBKey(primary_key, &value_str);
206 EXPECT_CALL(iterator, value()).WillOnce(Return(value_str));
207 }
208
ExpectIndexAndExistsEntries(leveldb::MockIterator & iterator,int64_t db,int64_t os,int64_t index,const IndexedDBKey & index_key,const IndexedDBKey & primary_key,int index_version,int exists_version)209 void ExpectIndexAndExistsEntries(leveldb::MockIterator& iterator,
210 int64_t db,
211 int64_t os,
212 int64_t index,
213 const IndexedDBKey& index_key,
214 const IndexedDBKey& primary_key,
215 int index_version,
216 int exists_version) {
217 ExpectIndexEntry(iterator, db, os, index, index_key, primary_key,
218 index_version);
219
220 testing::InSequence sequence_enforcer;
221
222 std::string encoded_primary_key;
223 EncodeIDBKey(primary_key, &encoded_primary_key);
224
225 std::string exists_value;
226 EncodeVarInt(exists_version, &exists_value);
227 EXPECT_CALL(
228 mock_db_,
229 Get(_, SliceEq(ExistsEntryKey::Encode(db, os, encoded_primary_key)), _))
230 .WillOnce(testing::DoAll(testing::SetArgPointee<2>(exists_value),
231 Return(Status::OK())));
232 }
233
234 protected:
235 std::unique_ptr<TransactionalLevelDBDatabase> in_memory_db_;
236 leveldb::MockLevelDB mock_db_;
237
238 std::unique_ptr<IndexedDBTombstoneSweeper> sweeper_;
239
240 StrictMock<MockTickClock> tick_clock_;
241
242 std::vector<IndexedDBDatabaseMetadata> metadata_;
243
244 // Used to verify recorded data.
245 base::HistogramTester histogram_tester_;
246
247 private:
248 base::test::TaskEnvironment task_environment_;
249 };
250
TEST_F(IndexedDBTombstoneSweeperTest,EmptyDB)251 TEST_F(IndexedDBTombstoneSweeperTest, EmptyDB) {
252 SetupMockDB();
253 sweeper_->SetMetadata(&metadata_);
254 EXPECT_TRUE(sweeper_->RunRound());
255
256 EXPECT_TRUE(
257 histogram_tester_.GetTotalCountsForPrefix("WebCore.IndexedDB.").empty());
258 }
259
TEST_F(IndexedDBTombstoneSweeperTest,NoTombstonesComplexDB)260 TEST_F(IndexedDBTombstoneSweeperTest, NoTombstonesComplexDB) {
261 SetupMockDB();
262 PopulateMultiDBMetdata();
263 sweeper_->SetMetadata(&metadata_);
264 SetClockExpectations();
265
266 // We'll have one index entry per index, and simulate reaching the end.
267 leveldb::MockIterator* mock_iterator = new leveldb::MockIterator();
268 EXPECT_CALL(mock_db_, NewIterator(testing::_))
269 .WillOnce(testing::Return(mock_iterator));
270
271 // First index.
272 {
273 testing::InSequence sequence_enforcer;
274 EXPECT_CALL(*mock_iterator,
275 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb1, kOs2, kIndex1))));
276 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
277
278 ExpectIndexAndExistsEntries(
279 *mock_iterator, kDb1, kOs2, kIndex1,
280 IndexedDBKey(10, blink::mojom::IDBKeyType::Number),
281 IndexedDBKey(20, blink::mojom::IDBKeyType::Number), 1, 1);
282 EXPECT_CALL(*mock_iterator, Next());
283
284 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
285 // Return the beginning of the second index, which should cause us to error
286 // & go restart our index seek.
287 ExpectIndexEntry(*mock_iterator, kDb1, kOs2, kIndex2,
288 IndexedDBKey(30, blink::mojom::IDBKeyType::Number),
289 IndexedDBKey(10, blink::mojom::IDBKeyType::Number), 1);
290 }
291
292 // Second index.
293 {
294 testing::InSequence sequence_enforcer;
295 EXPECT_CALL(*mock_iterator,
296 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb1, kOs2, kIndex2))));
297 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
298 ExpectIndexAndExistsEntries(
299 *mock_iterator, kDb1, kOs2, kIndex2,
300 IndexedDBKey(30, blink::mojom::IDBKeyType::Number),
301 IndexedDBKey(10, blink::mojom::IDBKeyType::Number), 1, 1);
302 EXPECT_CALL(*mock_iterator, Next());
303
304 // Return next key, which should make it error
305 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
306 ExpectIndexEntry(*mock_iterator, kDb2, kOs3, kIndex3,
307 IndexedDBKey(1501, blink::mojom::IDBKeyType::Number),
308 IndexedDBKey(15123, blink::mojom::IDBKeyType::Number), 12);
309 }
310
311 // Third index.
312 {
313 testing::InSequence sequence_enforcer;
314 EXPECT_CALL(*mock_iterator,
315 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb2, kOs3, kIndex3))));
316 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
317 ExpectIndexAndExistsEntries(
318 *mock_iterator, kDb2, kOs3, kIndex3,
319 IndexedDBKey(1501, blink::mojom::IDBKeyType::Number),
320 IndexedDBKey(15123, blink::mojom::IDBKeyType::Number), 12, 12);
321 EXPECT_CALL(*mock_iterator, Next());
322
323 // Return next key, which should make it error
324 EXPECT_CALL(*mock_iterator, Valid()).WillOnce(Return(false));
325 EXPECT_CALL(*mock_iterator, status()).WillOnce(Return(Status::OK()));
326 EXPECT_CALL(*mock_iterator, Valid()).WillOnce(Return(false));
327 }
328
329 ASSERT_TRUE(sweeper_->RunRound());
330 ExpectTaskTimeRecorded();
331 ExpectUmaTombstones(0, 0);
332 histogram_tester_.ExpectUniqueSample(
333 "WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent", 20, 1);
334 }
335
TEST_F(IndexedDBTombstoneSweeperTest,AllTombstonesComplexDB)336 TEST_F(IndexedDBTombstoneSweeperTest, AllTombstonesComplexDB) {
337 SetupMockDB();
338 PopulateMultiDBMetdata();
339 sweeper_->SetMetadata(&metadata_);
340 SetClockExpectations();
341
342 // We'll have one index entry per index, and simulate reaching the end.
343 leveldb::MockIterator* mock_iterator = new leveldb::MockIterator();
344 EXPECT_CALL(mock_db_, NewIterator(testing::_))
345 .WillOnce(testing::Return(mock_iterator));
346
347 // First index.
348 {
349 testing::InSequence sequence_enforcer;
350 EXPECT_CALL(*mock_iterator,
351 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb1, kOs2, kIndex1))));
352 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
353
354 ExpectIndexAndExistsEntries(
355 *mock_iterator, kDb1, kOs2, kIndex1,
356 IndexedDBKey(10, blink::mojom::IDBKeyType::Number),
357 IndexedDBKey(20, blink::mojom::IDBKeyType::Number), 1, 2);
358 EXPECT_CALL(*mock_iterator, Next());
359
360 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
361 // Return the beginning of the second index, which should cause us to error
362 // & go restart our index seek.
363 ExpectIndexEntry(*mock_iterator, kDb1, kOs2, kIndex2,
364 IndexedDBKey(30, blink::mojom::IDBKeyType::Number),
365 IndexedDBKey(10, blink::mojom::IDBKeyType::Number), 1);
366 }
367
368 // Second index.
369 {
370 testing::InSequence sequence_enforcer;
371 EXPECT_CALL(*mock_iterator,
372 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb1, kOs2, kIndex2))));
373 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
374 ExpectIndexAndExistsEntries(
375 *mock_iterator, kDb1, kOs2, kIndex2,
376 IndexedDBKey(30, blink::mojom::IDBKeyType::Number),
377 IndexedDBKey(10, blink::mojom::IDBKeyType::Number), 1, 2);
378 EXPECT_CALL(*mock_iterator, Next());
379
380 // Return next key, which should make it error
381 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
382 ExpectIndexEntry(*mock_iterator, kDb2, kOs3, kIndex3,
383 IndexedDBKey(1501, blink::mojom::IDBKeyType::Number),
384 IndexedDBKey(15123, blink::mojom::IDBKeyType::Number), 12);
385 }
386
387 // Third index.
388 {
389 testing::InSequence sequence_enforcer;
390 EXPECT_CALL(*mock_iterator,
391 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb2, kOs3, kIndex3))));
392 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
393 ExpectIndexAndExistsEntries(
394 *mock_iterator, kDb2, kOs3, kIndex3,
395 IndexedDBKey(1501, blink::mojom::IDBKeyType::Number),
396 IndexedDBKey(15123, blink::mojom::IDBKeyType::Number), 12, 13);
397 EXPECT_CALL(*mock_iterator, Next());
398
399 // Return next key, which should make it error
400 EXPECT_CALL(*mock_iterator, Valid()).WillOnce(Return(false));
401 EXPECT_CALL(*mock_iterator, status()).WillOnce(Return(Status::OK()));
402 EXPECT_CALL(*mock_iterator, Valid()).WillOnce(Return(false));
403 }
404
405 EXPECT_CALL(mock_db_, Write(_, _));
406
407 ASSERT_TRUE(sweeper_->RunRound());
408 ExpectTaskTimeRecorded();
409 ExpectUmaTombstones(3, kTombstoneSize * 3);
410 histogram_tester_.ExpectUniqueSample(
411 "WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent", 20, 1);
412 }
413
TEST_F(IndexedDBTombstoneSweeperTest,SimpleRealDBNoTombstones)414 TEST_F(IndexedDBTombstoneSweeperTest, SimpleRealDBNoTombstones) {
415 PopulateSingleIndexDBMetadata();
416 SetupRealDB();
417 sweeper_->SetMetadata(&metadata_);
418 SetClockExpectations();
419
420 for (int i = 0; i < kRoundIterations; i++) {
421 auto index_key = IndexedDBKey(i, blink::mojom::IDBKeyType::Number);
422 auto primary_key = IndexedDBKey(i + 1, blink::mojom::IDBKeyType::Number);
423 std::string value_str;
424 EncodeVarInt(1, &value_str);
425 EncodeIDBKey(primary_key, &value_str);
426 in_memory_db_->Put(
427 IndexDataKey::Encode(kDb1, kOs1, kIndex1, index_key, primary_key),
428 &value_str);
429
430 std::string exists_value;
431 std::string encoded_primary_key;
432 EncodeIDBKey(primary_key, &encoded_primary_key);
433 EncodeVarInt(1, &exists_value);
434 in_memory_db_->Put(ExistsEntryKey::Encode(kDb1, kOs1, encoded_primary_key),
435 &exists_value);
436 }
437
438 ASSERT_FALSE(sweeper_->RunRound());
439 EXPECT_TRUE(sweeper_->RunRound());
440
441 ExpectTaskTimeRecorded();
442 ExpectUmaTombstones(0, 0);
443 histogram_tester_.ExpectUniqueSample(
444 "WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent", 20, 1);
445 }
446
TEST_F(IndexedDBTombstoneSweeperTest,SimpleRealDBWithTombstones)447 TEST_F(IndexedDBTombstoneSweeperTest, SimpleRealDBWithTombstones) {
448 PopulateSingleIndexDBMetadata();
449 SetupRealDB();
450 sweeper_->SetMetadata(&metadata_);
451 SetClockExpectations();
452
453 int tombstones = 0;
454 for (int i = 0; i < kRoundIterations + 1; i++) {
455 auto index_key = IndexedDBKey(i, blink::mojom::IDBKeyType::Number);
456 auto primary_key = IndexedDBKey(i + 1, blink::mojom::IDBKeyType::Number);
457 std::string value_str;
458 EncodeVarInt(1, &value_str);
459 EncodeIDBKey(primary_key, &value_str);
460 in_memory_db_->Put(
461 IndexDataKey::Encode(kDb1, kOs1, kIndex1, index_key, primary_key),
462 &value_str);
463
464 std::string exists_value;
465 std::string encoded_primary_key;
466 EncodeIDBKey(primary_key, &encoded_primary_key);
467 bool tombstone = i % 2 != 0;
468 tombstones += i % 2 ? 1 : 0;
469 EncodeVarInt(tombstone ? 2 : 1, &exists_value);
470 in_memory_db_->Put(ExistsEntryKey::Encode(kDb1, kOs1, encoded_primary_key),
471 &exists_value);
472 }
473
474 ASSERT_FALSE(sweeper_->RunRound());
475 EXPECT_TRUE(sweeper_->RunRound());
476
477 ExpectTaskTimeRecorded();
478 ExpectUmaTombstones(tombstones, kTombstoneSize * tombstones);
479 histogram_tester_.ExpectUniqueSample(
480 "WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent", 20, 1);
481
482 for (int i = 0; i < kRoundIterations + 1; i++) {
483 if (i % 2 == 1) {
484 std::string out;
485 bool found = false;
486 auto index_key = IndexedDBKey(i, blink::mojom::IDBKeyType::Number);
487 auto primary_key = IndexedDBKey(i + 1, blink::mojom::IDBKeyType::Number);
488 EXPECT_TRUE(in_memory_db_
489 ->Get(IndexDataKey::Encode(kDb1, kOs1, kIndex1, index_key,
490 primary_key),
491 &out, &found)
492 .ok());
493 EXPECT_TRUE(!found);
494 }
495 }
496 }
497
TEST_F(IndexedDBTombstoneSweeperTest,HitMaxIters)498 TEST_F(IndexedDBTombstoneSweeperTest, HitMaxIters) {
499 PopulateSingleIndexDBMetadata();
500 SetupRealDB();
501 sweeper_->SetMetadata(&metadata_);
502 SetFirstClockExpectation();
503
504 for (int i = 0; i < kMaxIterations + 1; i++) {
505 auto index_key = IndexedDBKey(i, blink::mojom::IDBKeyType::Number);
506 auto primary_key = IndexedDBKey(i + 1, blink::mojom::IDBKeyType::Number);
507 std::string value_str;
508 EncodeVarInt(1, &value_str);
509 EncodeIDBKey(primary_key, &value_str);
510 in_memory_db_->Put(
511 IndexDataKey::Encode(kDb1, kOs1, kIndex1, index_key, primary_key),
512 &value_str);
513
514 std::string exists_value;
515 std::string encoded_primary_key;
516 EncodeIDBKey(primary_key, &encoded_primary_key);
517 EncodeVarInt(i % 2 == 0 ? 2 : 1, &exists_value);
518 in_memory_db_->Put(ExistsEntryKey::Encode(kDb1, kOs1, encoded_primary_key),
519 &exists_value);
520 }
521
522 while (!sweeper_->RunRound())
523 ;
524
525 ExpectUmaTombstones(41, kTombstoneSize * 41, true);
526 histogram_tester_.ExpectUniqueSample(
527 "WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent", 0, 1);
528 }
529
TEST_F(IndexedDBTombstoneSweeperTest,LevelDBError)530 TEST_F(IndexedDBTombstoneSweeperTest, LevelDBError) {
531 SetupMockDB();
532 PopulateMultiDBMetdata();
533 sweeper_->SetMetadata(&metadata_);
534 SetFirstClockExpectation();
535
536 // We'll have one index entry per index, and simulate reaching the end.
537 leveldb::MockIterator* mock_iterator = new leveldb::MockIterator();
538 EXPECT_CALL(mock_db_, NewIterator(testing::_))
539 .WillOnce(testing::Return(mock_iterator));
540
541 // First index.
542 {
543 testing::InSequence sequence_enforcer;
544 EXPECT_CALL(*mock_iterator,
545 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb1, kOs2, kIndex1))));
546 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
547
548 ExpectIndexAndExistsEntries(
549 *mock_iterator, kDb1, kOs2, kIndex1,
550 IndexedDBKey(10, blink::mojom::IDBKeyType::Number),
551 IndexedDBKey(20, blink::mojom::IDBKeyType::Number), 1, 1);
552 EXPECT_CALL(*mock_iterator, Next());
553
554 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
555 // Return the beginning of the second index, which should cause us to error
556 // & go restart our index seek.
557 ExpectIndexEntry(*mock_iterator, kDb1, kOs2, kIndex2,
558 IndexedDBKey(30, blink::mojom::IDBKeyType::Number),
559 IndexedDBKey(10, blink::mojom::IDBKeyType::Number), 1);
560 }
561
562 // Second index.
563 {
564 testing::InSequence sequence_enforcer;
565 EXPECT_CALL(*mock_iterator,
566 Seek(SliceEq(IndexDataKey::EncodeMinKey(kDb1, kOs2, kIndex2))));
567 EXPECT_CALL(*mock_iterator, Valid()).Times(2).WillRepeatedly(Return(true));
568 ExpectIndexAndExistsEntries(
569 *mock_iterator, kDb1, kOs2, kIndex2,
570 IndexedDBKey(30, blink::mojom::IDBKeyType::Number),
571 IndexedDBKey(10, blink::mojom::IDBKeyType::Number), 1, 1);
572 EXPECT_CALL(*mock_iterator, Next());
573
574 // Return read error.
575 EXPECT_CALL(*mock_iterator, Valid()).WillOnce(Return(false));
576 EXPECT_CALL(*mock_iterator, status())
577 .WillOnce(Return(Status::Corruption("Test error")));
578 }
579
580 ASSERT_TRUE(sweeper_->RunRound());
581
582 histogram_tester_.ExpectUniqueSample(
583 "WebCore.IndexedDB.TombstoneSweeper.SweepError",
584 leveldb_env::GetLevelDBStatusUMAValue(Status::Corruption("")), 1);
585 // Only finished scanning the first index.
586 histogram_tester_.ExpectUniqueSample(
587 "WebCore.IndexedDB.TombstoneSweeper.IndexScanPercent", 1 * 20 / 3, 1);
588 }
589
590 } // namespace indexed_db_tombstone_sweeper_unittest
591 } // namespace content
592