Commit 2eb46b48 authored by Joshua Bell's avatar Joshua Bell Committed by Commit Bot

Indexed DB: Simplify cross-sequence test code

Now that base::BindLambdaForTesting() can be used, tasks posted to the
Indexed DB task runner in tests don't need to explicitly bring along
ambient state. Also, use an explicit RunLoop's Run()/Quit() instead of
RunAllTasksUntilIdle() where possible, and 'auto' for readability.

Change-Id: Iede419cda01c166e8fe2047c901f528f687f940d
Reviewed-on: https://chromium-review.googlesource.com/c/1298416
Commit-Queue: Daniel Murphy <dmurph@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603052}
parent 221ff95e
......@@ -18,6 +18,7 @@
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/test/bind_test_util.h"
#include "base/time/default_clock.h"
#include "content/browser/indexed_db/indexed_db_class_factory.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
......@@ -112,8 +113,8 @@ class TestableIndexedDBBackingStore : public IndexedDBBackingStore {
std::unique_ptr<LevelDBDatabase> db;
bool is_disk_full = false;
*status = leveldb_factory->OpenLevelDB(
file_path, comparator.get(), &db, &is_disk_full);
*status = leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db,
&is_disk_full);
if (!db || !status->ok())
return scoped_refptr<TestableIndexedDBBackingStore>();
......@@ -174,13 +175,12 @@ class TestableIndexedDBBackingStore : public IndexedDBBackingStore {
}
private:
TestableIndexedDBBackingStore(
IndexedDBFactory* indexed_db_factory,
const Origin& origin,
const base::FilePath& blob_path,
std::unique_ptr<LevelDBDatabase> db,
std::unique_ptr<LevelDBComparator> comparator,
base::SequencedTaskRunner* task_runner)
TestableIndexedDBBackingStore(IndexedDBFactory* indexed_db_factory,
const Origin& origin,
const base::FilePath& blob_path,
std::unique_ptr<LevelDBDatabase> db,
std::unique_ptr<LevelDBComparator> comparator,
base::SequencedTaskRunner* task_runner)
: IndexedDBBackingStore(indexed_db_factory,
origin,
blob_path,
......@@ -209,8 +209,8 @@ class TestIDBFactory : public IndexedDBFactoryImpl {
IndexedDBDataLossInfo data_loss_info;
bool disk_full;
leveldb::Status status;
scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore(
origin, context()->data_path(), &data_loss_info, &disk_full, &status);
auto backing_store = OpenBackingStore(origin, context()->data_path(),
&data_loss_info, &disk_full, &status);
scoped_refptr<TestableIndexedDBBackingStore> testable_store =
static_cast<TestableIndexedDBBackingStore*>(backing_store.get());
return testable_store;
......@@ -246,30 +246,28 @@ class IndexedDBBackingStoreTest : public testing::Test {
void CreateFactoryAndBackingStore() {
// Factory and backing store must be created on IDB task runner.
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTest* test) {
const Origin origin = Origin::Create(GURL("http://localhost:81"));
test->idb_factory_ = base::MakeRefCounted<TestIDBFactory>(
test->idb_context_.get());
test->backing_store_ =
test->idb_factory_->OpenBackingStoreForTest(origin);
},
base::Unretained(this)));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const Origin origin = Origin::Create(GURL("http://localhost:81"));
idb_factory_ =
base::MakeRefCounted<TestIDBFactory>(idb_context_.get());
backing_store_ = idb_factory_->OpenBackingStoreForTest(origin);
loop.Quit();
}));
loop.Run();
}
void DestroyFactoryAndBackingStore() {
// Factory and backing store must be destroyed on IDB task runner.
idb_context_->TaskRunner()->PostTask(
FROM_HERE, base::BindOnce(
[](IndexedDBBackingStoreTest* test) {
test->idb_factory_ = nullptr;
test->backing_store_ = nullptr;
},
base::Unretained(this)));
RunAllTasksUntilIdle();
// Factory and backing store must be destroyed on IDB task runner
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(FROM_HERE,
base::BindLambdaForTesting([&]() {
idb_factory_.reset();
backing_store_.reset();
loop.Quit();
}));
loop.Run();
}
void SetUp() override {
......@@ -299,13 +297,6 @@ class IndexedDBBackingStoreTest : public testing::Test {
return backing_store_.get();
}
// Sample keys and values that are consistent. Public so that posted lambdas
// passed |this| can access them.
IndexedDBKey key1_;
IndexedDBKey key2_;
IndexedDBValue value1_;
IndexedDBValue value2_;
protected:
TestBrowserThreadBundle thread_bundle_;
......@@ -317,6 +308,12 @@ class IndexedDBBackingStoreTest : public testing::Test {
scoped_refptr<TestableIndexedDBBackingStore> backing_store_;
// Sample keys and values that are consistent.
IndexedDBKey key1_;
IndexedDBKey key2_;
IndexedDBValue value1_;
IndexedDBValue value2_;
private:
DISALLOW_COPY_AND_ASSIGN(IndexedDBBackingStoreTest);
};
......@@ -469,137 +466,118 @@ class TestCallback : public IndexedDBBackingStore::BlobWriteCallback {
};
TEST_F(IndexedDBBackingStoreTest, PutGetConsistency) {
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, IndexedDBKey key,
IndexedDBValue value) {
{
IndexedDBBackingStore::Transaction transaction1(backing_store);
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store->PutRecord(
&transaction1, 1, 1, key, &value, &record);
EXPECT_TRUE(s.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
}
{
IndexedDBBackingStore::Transaction transaction2(backing_store);
transaction2.Begin();
IndexedDBValue result_value;
EXPECT_TRUE(
backing_store
->GetRecord(&transaction2, 1, 1, key, &result_value)
.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(value.bits, result_value.bits);
}
},
base::Unretained(backing_store()), key1_, value1_));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const IndexedDBKey key = key1_;
IndexedDBValue value = value1_;
{
IndexedDBBackingStore::Transaction transaction1(backing_store());
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store()->PutRecord(&transaction1, 1, 1,
key, &value, &record);
EXPECT_TRUE(s.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
}
{
IndexedDBBackingStore::Transaction transaction2(backing_store());
transaction2.Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store()
->GetRecord(&transaction2, 1, 1, key, &result_value)
.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(value.bits, result_value.bits);
}
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBBackingStoreTestWithBlobs, PutGetConsistencyWithBlobs) {
struct TestState {
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3;
scoped_refptr<TestCallback> callback3;
} state;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3;
scoped_refptr<TestCallback> callback3;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Initiate transaction1 - writing blobs.
state->transaction1 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
EXPECT_TRUE(test->backing_store()
->PutRecord(state->transaction1.get(), 1, 1,
test->key3_, &test->value3_, &record)
.ok());
state->callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction1->CommitPhaseOne(state->callback1).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Initiate transaction1 - writing blobs.
transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
EXPECT_TRUE(
backing_store()
->PutRecord(transaction1.get(), 1, 1, key3_, &value3_, &record)
.ok());
callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1->CommitPhaseOne(callback1).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Finish up transaction1, verifying blob writes.
EXPECT_TRUE(state->callback1->called);
EXPECT_TRUE(state->callback1->succeeded);
EXPECT_TRUE(test->CheckBlobWrites());
EXPECT_TRUE(state->transaction1->CommitPhaseTwo().ok());
// Initiate transaction2, reading blobs.
IndexedDBBackingStore::Transaction transaction2(
test->backing_store());
transaction2.Begin();
IndexedDBValue result_value;
EXPECT_TRUE(
test->backing_store()
->GetRecord(&transaction2, 1, 1, test->key3_, &result_value)
.ok());
// Finish up transaction2, verifying blob reads.
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(test->value3_.bits, result_value.bits);
EXPECT_TRUE(test->CheckBlobInfoMatches(result_value.blob_info));
EXPECT_TRUE(
test->CheckBlobReadsMatchWrites(result_value.blob_info));
// Initiate transaction3, deleting blobs.
state->transaction3 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction3->Begin();
EXPECT_TRUE(test->backing_store()
->DeleteRange(state->transaction3.get(), 1, 1,
IndexedDBKeyRange(test->key3_))
.ok());
state->callback3 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction3->CommitPhaseOne(state->callback3).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish up transaction1, verifying blob writes.
EXPECT_TRUE(callback1->called);
EXPECT_TRUE(callback1->succeeded);
EXPECT_TRUE(CheckBlobWrites());
EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
// Initiate transaction2, reading blobs.
IndexedDBBackingStore::Transaction transaction2(backing_store());
transaction2.Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store()
->GetRecord(&transaction2, 1, 1, key3_, &result_value)
.ok());
// Finish up transaction2, verifying blob reads.
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(value3_.bits, result_value.bits);
EXPECT_TRUE(CheckBlobInfoMatches(result_value.blob_info));
EXPECT_TRUE(CheckBlobReadsMatchWrites(result_value.blob_info));
// Initiate transaction3, deleting blobs.
transaction3 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction3->Begin();
EXPECT_TRUE(backing_store()
->DeleteRange(transaction3.get(), 1, 1,
IndexedDBKeyRange(key3_))
.ok());
callback3 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction3->CommitPhaseOne(callback3).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Finish up transaction 3, verifying blob deletes.
EXPECT_TRUE(state->transaction3->CommitPhaseTwo().ok());
EXPECT_TRUE(test->CheckBlobRemovals());
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish up transaction 3, verifying blob deletes.
EXPECT_TRUE(transaction3->CommitPhaseTwo().ok());
EXPECT_TRUE(CheckBlobRemovals());
// Clean up on the IDB sequence.
transaction1.reset();
callback1.reset();
transaction3.reset();
callback3.reset();
}));
RunAllTasksUntilIdle();
}
......@@ -619,124 +597,106 @@ TEST_F(IndexedDBBackingStoreTestWithBlobs, DeleteRange) {
const int64_t object_store_id = i + 1;
const IndexedDBKeyRange& range = ranges[i];
struct TestState {
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2;
scoped_refptr<TestCallback> callback2;
std::vector<std::unique_ptr<storage::BlobDataHandle>> blobs;
} state;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2;
scoped_refptr<TestCallback> callback2;
std::vector<std::unique_ptr<storage::BlobDataHandle>> blobs;
for (size_t j = 0; j < 4; ++j)
state.blobs.push_back(CreateBlob());
blobs.push_back(CreateBlob());
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestableIndexedDBBackingStore* backing_store, TestState* state,
const std::vector<IndexedDBKey>& keys, int64_t database_id,
int64_t object_store_id) {
// Reset from previous iteration.
backing_store->ClearWrites();
backing_store->ClearRemovals();
std::vector<IndexedDBValue> values = {
IndexedDBValue("value0",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[0]),
base::UTF8ToUTF16("type 0"), 1)}),
IndexedDBValue("value1",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[1]),
base::UTF8ToUTF16("type 1"), 1)}),
IndexedDBValue("value2",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[2]),
base::UTF8ToUTF16("type 2"), 1)}),
IndexedDBValue("value3",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[3]),
base::UTF8ToUTF16("type 3"), 1)})};
ASSERT_GE(keys.size(), values.size());
// Initiate transaction1 - write records.
state->transaction1 =
std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store);
state->transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
for (size_t i = 0; i < values.size(); ++i) {
EXPECT_TRUE(backing_store
->PutRecord(state->transaction1.get(),
database_id, object_store_id,
keys[i], &values[i], &record)
.ok());
}
// Start committing transaction1.
state->callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction1->CommitPhaseOne(state->callback1).ok());
},
base::Unretained(backing_store()), base::Unretained(&state),
base::ConstRef(keys), database_id, object_store_id));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Reset from previous iteration.
backing_store()->ClearWrites();
backing_store()->ClearRemovals();
std::vector<IndexedDBValue> values = {
IndexedDBValue(
"value0",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[0]),
base::UTF8ToUTF16("type 0"), 1)}),
IndexedDBValue(
"value1",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[1]),
base::UTF8ToUTF16("type 1"), 1)}),
IndexedDBValue(
"value2",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[2]),
base::UTF8ToUTF16("type 2"), 1)}),
IndexedDBValue(
"value3",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[3]),
base::UTF8ToUTF16("type 3"), 1)})};
ASSERT_GE(keys.size(), values.size());
// Initiate transaction1 - write records.
transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
for (size_t i = 0; i < values.size(); ++i) {
EXPECT_TRUE(backing_store()
->PutRecord(transaction1.get(), database_id,
object_store_id, keys[i], &values[i],
&record)
.ok());
}
// Start committing transaction1.
callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1->CommitPhaseOne(callback1).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestableIndexedDBBackingStore* backing_store, TestState* state,
IndexedDBKeyRange range, int64_t database_id,
int64_t object_store_id) {
// Finish committing transaction1.
EXPECT_TRUE(state->callback1->called);
EXPECT_TRUE(state->callback1->succeeded);
EXPECT_TRUE(state->transaction1->CommitPhaseTwo().ok());
// Initiate transaction 2 - delete range.
state->transaction2 =
std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store);
state->transaction2->Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store
->DeleteRange(state->transaction2.get(),
database_id, object_store_id, range)
.ok());
// Start committing transaction2.
state->callback2 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction2->CommitPhaseOne(state->callback2).ok());
},
base::Unretained(backing_store()), base::Unretained(&state), range,
database_id, object_store_id));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish committing transaction1.
EXPECT_TRUE(callback1->called);
EXPECT_TRUE(callback1->succeeded);
EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
// Initiate transaction 2 - delete range.
transaction2 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction2->Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store()
->DeleteRange(transaction2.get(), database_id,
object_store_id, range)
.ok());
// Start committing transaction2.
callback2 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2->CommitPhaseOne(callback2).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestableIndexedDBBackingStore* backing_store, TestState* state) {
// Finish committing transaction2.
EXPECT_TRUE(state->callback2->called);
EXPECT_TRUE(state->callback2->succeeded);
EXPECT_TRUE(state->transaction2->CommitPhaseTwo().ok());
// Verify blob removals.
ASSERT_EQ(2UL, backing_store->removals().size());
EXPECT_EQ(backing_store->writes()[1].key(),
backing_store->removals()[0]);
EXPECT_EQ(backing_store->writes()[2].key(),
backing_store->removals()[1]);
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(backing_store()), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish committing transaction2.
EXPECT_TRUE(callback2->called);
EXPECT_TRUE(callback2->succeeded);
EXPECT_TRUE(transaction2->CommitPhaseTwo().ok());
// Verify blob removals.
ASSERT_EQ(2UL, backing_store()->removals().size());
EXPECT_EQ(backing_store()->writes()[1].key(),
backing_store()->removals()[0]);
EXPECT_EQ(backing_store()->writes()[2].key(),
backing_store()->removals()[1]);
// Clean up on the IDB sequence.
transaction1.reset();
callback1.reset();
transaction2.reset();
callback2.reset();
}));
RunAllTasksUntilIdle();
}
}
......@@ -756,609 +716,552 @@ TEST_F(IndexedDBBackingStoreTestWithBlobs, DeleteRangeEmptyRange) {
const int64_t object_store_id = i + 1;
const IndexedDBKeyRange& range = ranges[i];
struct TestState {
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2;
scoped_refptr<TestCallback> callback2;
std::vector<std::unique_ptr<storage::BlobDataHandle>> blobs;
} state;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2;
scoped_refptr<TestCallback> callback2;
std::vector<std::unique_ptr<storage::BlobDataHandle>> blobs;
for (size_t j = 0; j < 4; ++j)
state.blobs.push_back(CreateBlob());
blobs.push_back(CreateBlob());
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestableIndexedDBBackingStore* backing_store, TestState* state,
const std::vector<IndexedDBKey>& keys, int64_t database_id,
int64_t object_store_id) {
// Reset from previous iteration.
backing_store->ClearWrites();
backing_store->ClearRemovals();
std::vector<IndexedDBValue> values = {
IndexedDBValue("value0",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[0]),
base::UTF8ToUTF16("type 0"), 1)}),
IndexedDBValue("value1",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[1]),
base::UTF8ToUTF16("type 1"), 1)}),
IndexedDBValue("value2",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[2]),
base::UTF8ToUTF16("type 2"), 1)}),
IndexedDBValue("value3",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(
*state->blobs[3]),
base::UTF8ToUTF16("type 3"), 1)})};
ASSERT_GE(keys.size(), values.size());
// Initiate transaction1 - write records.
state->transaction1 =
std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store);
state->transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
for (size_t i = 0; i < values.size(); ++i) {
EXPECT_TRUE(backing_store
->PutRecord(state->transaction1.get(),
database_id, object_store_id,
keys[i], &values[i], &record)
.ok());
}
// Start committing transaction1.
state->callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction1->CommitPhaseOne(state->callback1).ok());
},
base::Unretained(backing_store()), base::Unretained(&state),
base::ConstRef(keys), database_id, object_store_id));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Reset from previous iteration.
backing_store()->ClearWrites();
backing_store()->ClearRemovals();
std::vector<IndexedDBValue> values = {
IndexedDBValue(
"value0",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[0]),
base::UTF8ToUTF16("type 0"), 1)}),
IndexedDBValue(
"value1",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[1]),
base::UTF8ToUTF16("type 1"), 1)}),
IndexedDBValue(
"value2",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[2]),
base::UTF8ToUTF16("type 2"), 1)}),
IndexedDBValue(
"value3",
{IndexedDBBlobInfo(
std::make_unique<storage::BlobDataHandle>(*blobs[3]),
base::UTF8ToUTF16("type 3"), 1)})};
ASSERT_GE(keys.size(), values.size());
// Initiate transaction1 - write records.
transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
for (size_t i = 0; i < values.size(); ++i) {
EXPECT_TRUE(backing_store()
->PutRecord(transaction1.get(), database_id,
object_store_id, keys[i], &values[i],
&record)
.ok());
}
// Start committing transaction1.
callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1->CommitPhaseOne(callback1).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestableIndexedDBBackingStore* backing_store, TestState* state,
IndexedDBKeyRange range, int64_t database_id,
int64_t object_store_id) {
// Finish committing transaction1.
EXPECT_TRUE(state->callback1->called);
EXPECT_TRUE(state->callback1->succeeded);
EXPECT_TRUE(state->transaction1->CommitPhaseTwo().ok());
// Initiate transaction 2 - delete range.
state->transaction2 =
std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store);
state->transaction2->Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store
->DeleteRange(state->transaction2.get(),
database_id, object_store_id, range)
.ok());
// Start committing transaction2.
state->callback2 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction2->CommitPhaseOne(state->callback2).ok());
},
base::Unretained(backing_store()), base::Unretained(&state), range,
database_id, object_store_id));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish committing transaction1.
EXPECT_TRUE(callback1->called);
EXPECT_TRUE(callback1->succeeded);
EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
// Initiate transaction 2 - delete range.
transaction2 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction2->Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store()
->DeleteRange(transaction2.get(), database_id,
object_store_id, range)
.ok());
// Start committing transaction2.
callback2 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2->CommitPhaseOne(callback2).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](TestableIndexedDBBackingStore* backing_store, TestState* state) {
// Finish committing transaction2.
EXPECT_TRUE(state->callback2->called);
EXPECT_TRUE(state->callback2->succeeded);
EXPECT_TRUE(state->transaction2->CommitPhaseTwo().ok());
// Verify blob removals.
EXPECT_EQ(0UL, backing_store->removals().size());
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(backing_store()), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish committing transaction2.
EXPECT_TRUE(callback2->called);
EXPECT_TRUE(callback2->succeeded);
EXPECT_TRUE(transaction2->CommitPhaseTwo().ok());
// Verify blob removals.
EXPECT_EQ(0UL, backing_store()->removals().size());
// Clean on the IDB sequence.
transaction1.reset();
callback1.reset();
transaction2.reset();
callback2.reset();
}));
RunAllTasksUntilIdle();
}
}
TEST_F(IndexedDBBackingStoreTestWithBlobs, BlobJournalInterleavedTransactions) {
struct TestState {
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2;
scoped_refptr<TestCallback> callback2;
} state;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction2;
scoped_refptr<TestCallback> callback2;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Initiate transaction1.
state->transaction1 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record1;
EXPECT_TRUE(test->backing_store()
->PutRecord(state->transaction1.get(), 1, 1,
test->key3_, &test->value3_, &record1)
.ok());
state->callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction1->CommitPhaseOne(state->callback1).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Initiate transaction1.
transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record1;
EXPECT_TRUE(
backing_store()
->PutRecord(transaction1.get(), 1, 1, key3_, &value3_, &record1)
.ok());
callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1->CommitPhaseOne(callback1).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Verify transaction1 phase one completed.
EXPECT_TRUE(state->callback1->called);
EXPECT_TRUE(state->callback1->succeeded);
EXPECT_TRUE(test->CheckBlobWrites());
EXPECT_EQ(0U, test->backing_store()->removals().size());
// Initiate transaction2.
state->transaction2 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction2->Begin();
IndexedDBBackingStore::RecordIdentifier record2;
EXPECT_TRUE(test->backing_store()
->PutRecord(state->transaction2.get(), 1, 1,
test->key1_, &test->value1_, &record2)
.ok());
state->callback2 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction2->CommitPhaseOne(state->callback2).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Verify transaction1 phase one completed.
EXPECT_TRUE(callback1->called);
EXPECT_TRUE(callback1->succeeded);
EXPECT_TRUE(CheckBlobWrites());
EXPECT_EQ(0U, backing_store()->removals().size());
// Initiate transaction2.
transaction2 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction2->Begin();
IndexedDBBackingStore::RecordIdentifier record2;
EXPECT_TRUE(
backing_store()
->PutRecord(transaction2.get(), 1, 1, key1_, &value1_, &record2)
.ok());
callback2 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2->CommitPhaseOne(callback2).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Verify transaction2 phase one completed.
EXPECT_TRUE(state->callback2->called);
EXPECT_TRUE(state->callback2->succeeded);
EXPECT_TRUE(test->CheckBlobWrites());
EXPECT_EQ(0U, test->backing_store()->removals().size());
// Finalize both transactions.
EXPECT_TRUE(state->transaction1->CommitPhaseTwo().ok());
EXPECT_EQ(0U, test->backing_store()->removals().size());
EXPECT_TRUE(state->transaction2->CommitPhaseTwo().ok());
EXPECT_EQ(0U, test->backing_store()->removals().size());
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Verify transaction2 phase one completed.
EXPECT_TRUE(callback2->called);
EXPECT_TRUE(callback2->succeeded);
EXPECT_TRUE(CheckBlobWrites());
EXPECT_EQ(0U, backing_store()->removals().size());
// Finalize both transactions.
EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
EXPECT_EQ(0U, backing_store()->removals().size());
EXPECT_TRUE(transaction2->CommitPhaseTwo().ok());
EXPECT_EQ(0U, backing_store()->removals().size());
// Clean up on the IDB sequence.
transaction1.reset();
callback1.reset();
transaction2.reset();
callback2.reset();
}));
RunAllTasksUntilIdle();
}
TEST_F(IndexedDBBackingStoreTestWithBlobs, LiveBlobJournal) {
struct TestState {
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3;
scoped_refptr<TestCallback> callback3;
IndexedDBValue read_result_value;
} state;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3;
scoped_refptr<TestCallback> callback3;
IndexedDBValue read_result_value;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
state->transaction1 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
EXPECT_TRUE(test->backing_store()
->PutRecord(state->transaction1.get(), 1, 1,
test->key3_, &test->value3_, &record)
.ok());
state->callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction1->CommitPhaseOne(state->callback1).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
EXPECT_TRUE(
backing_store()
->PutRecord(transaction1.get(), 1, 1, key3_, &value3_, &record)
.ok());
callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1->CommitPhaseOne(callback1).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
EXPECT_TRUE(state->callback1->called);
EXPECT_TRUE(state->callback1->succeeded);
EXPECT_TRUE(test->CheckBlobWrites());
EXPECT_TRUE(state->transaction1->CommitPhaseTwo().ok());
IndexedDBBackingStore::Transaction transaction2(
test->backing_store());
transaction2.Begin();
EXPECT_TRUE(test->backing_store()
->GetRecord(&transaction2, 1, 1, test->key3_,
&state->read_result_value)
.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(test->value3_.bits, state->read_result_value.bits);
EXPECT_TRUE(
test->CheckBlobInfoMatches(state->read_result_value.blob_info));
EXPECT_TRUE(test->CheckBlobReadsMatchWrites(
state->read_result_value.blob_info));
for (size_t i = 0; i < state->read_result_value.blob_info.size();
++i) {
state->read_result_value.blob_info[i].mark_used_callback().Run();
}
state->transaction3 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction3->Begin();
EXPECT_TRUE(test->backing_store()
->DeleteRange(state->transaction3.get(), 1, 1,
IndexedDBKeyRange(test->key3_))
.ok());
state->callback3 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction3->CommitPhaseOne(state->callback3).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_TRUE(callback1->called);
EXPECT_TRUE(callback1->succeeded);
EXPECT_TRUE(CheckBlobWrites());
EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
IndexedDBBackingStore::Transaction transaction2(backing_store());
transaction2.Begin();
EXPECT_TRUE(
backing_store()
->GetRecord(&transaction2, 1, 1, key3_, &read_result_value)
.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(value3_.bits, read_result_value.bits);
EXPECT_TRUE(CheckBlobInfoMatches(read_result_value.blob_info));
EXPECT_TRUE(CheckBlobReadsMatchWrites(read_result_value.blob_info));
for (size_t i = 0; i < read_result_value.blob_info.size(); ++i) {
read_result_value.blob_info[i].mark_used_callback().Run();
}
transaction3 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction3->Begin();
EXPECT_TRUE(backing_store()
->DeleteRange(transaction3.get(), 1, 1,
IndexedDBKeyRange(key3_))
.ok());
callback3 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction3->CommitPhaseOne(callback3).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
EXPECT_TRUE(state->callback3->called);
EXPECT_TRUE(state->callback3->succeeded);
EXPECT_TRUE(state->transaction3->CommitPhaseTwo().ok());
EXPECT_EQ(0U, test->backing_store()->removals().size());
for (size_t i = 0; i < state->read_result_value.blob_info.size();
++i) {
state->read_result_value.blob_info[i].release_callback().Run(
state->read_result_value.blob_info[i].file_path());
}
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_TRUE(callback3->called);
EXPECT_TRUE(callback3->succeeded);
EXPECT_TRUE(transaction3->CommitPhaseTwo().ok());
EXPECT_EQ(0U, backing_store()->removals().size());
for (size_t i = 0; i < read_result_value.blob_info.size(); ++i) {
read_result_value.blob_info[i].release_callback().Run(
read_result_value.blob_info[i].file_path());
}
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
EXPECT_TRUE(test->backing_store()->IsBlobCleanupPending());
FROM_HERE, base::BindLambdaForTesting([&]() {
EXPECT_TRUE(backing_store()->IsBlobCleanupPending());
#if DCHECK_IS_ON()
EXPECT_EQ(3,
test->backing_store()
->NumAggregatedJournalCleaningRequestsForTesting());
EXPECT_EQ(
3,
backing_store()->NumAggregatedJournalCleaningRequestsForTesting());
#endif
for (int i = 3; i < IndexedDBBackingStore::kMaxJournalCleanRequests;
++i) {
test->backing_store()->StartJournalCleaningTimer();
}
EXPECT_NE(0U, test->backing_store()->removals().size());
EXPECT_TRUE(test->CheckBlobRemovals());
for (int i = 3; i < IndexedDBBackingStore::kMaxJournalCleanRequests;
++i) {
backing_store()->StartJournalCleaningTimer();
}
EXPECT_NE(0U, backing_store()->removals().size());
EXPECT_TRUE(CheckBlobRemovals());
#if DCHECK_IS_ON()
EXPECT_EQ(0,
test->backing_store()->NumBlobFilesDeletedForTesting());
EXPECT_EQ(0, backing_store()->NumBlobFilesDeletedForTesting());
#endif
EXPECT_FALSE(test->backing_store()->IsBlobCleanupPending());
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(this), base::Unretained(&state)));
EXPECT_FALSE(backing_store()->IsBlobCleanupPending());
// Clean on the IDB sequence.
transaction1.reset();
callback1.reset();
transaction3.reset();
callback3.reset();
IndexedDBValue read_result_value;
}));
RunAllTasksUntilIdle();
}
// Make sure that using very high ( more than 32 bit ) values for database_id
// and object_store_id still work.
TEST_F(IndexedDBBackingStoreTest, HighIds) {
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, IndexedDBKey key1,
IndexedDBKey key2, IndexedDBValue value1) {
const int64_t high_database_id = 1ULL << 35;
const int64_t high_object_store_id = 1ULL << 39;
// index_ids are capped at 32 bits for storage purposes.
const int64_t high_index_id = 1ULL << 29;
const int64_t invalid_high_index_id = 1ULL << 37;
const IndexedDBKey& index_key = key2;
std::string index_key_raw;
EncodeIDBKey(index_key, &index_key_raw);
{
IndexedDBBackingStore::Transaction transaction1(backing_store);
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store->PutRecord(
&transaction1, high_database_id, high_object_store_id, key1,
&value1, &record);
EXPECT_TRUE(s.ok());
s = backing_store->PutIndexDataForRecord(
&transaction1, high_database_id, high_object_store_id,
invalid_high_index_id, index_key, record);
EXPECT_FALSE(s.ok());
s = backing_store->PutIndexDataForRecord(
&transaction1, high_database_id, high_object_store_id,
high_index_id, index_key, record);
EXPECT_TRUE(s.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
}
{
IndexedDBBackingStore::Transaction transaction2(backing_store);
transaction2.Begin();
IndexedDBValue result_value;
leveldb::Status s = backing_store->GetRecord(
&transaction2, high_database_id, high_object_store_id, key1,
&result_value);
EXPECT_TRUE(s.ok());
EXPECT_EQ(value1.bits, result_value.bits);
std::unique_ptr<IndexedDBKey> new_primary_key;
s = backing_store->GetPrimaryKeyViaIndex(
&transaction2, high_database_id, high_object_store_id,
invalid_high_index_id, index_key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store->GetPrimaryKeyViaIndex(
&transaction2, high_database_id, high_object_store_id,
high_index_id, index_key, &new_primary_key);
EXPECT_TRUE(s.ok());
EXPECT_TRUE(new_primary_key->Equals(key1));
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
}
},
base::Unretained(backing_store()), key1_, key2_, value1_));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
IndexedDBKey key1 = key1_;
IndexedDBKey key2 = key2_;
IndexedDBValue value1 = value1_;
const int64_t high_database_id = 1ULL << 35;
const int64_t high_object_store_id = 1ULL << 39;
// index_ids are capped at 32 bits for storage purposes.
const int64_t high_index_id = 1ULL << 29;
const int64_t invalid_high_index_id = 1ULL << 37;
const IndexedDBKey& index_key = key2;
std::string index_key_raw;
EncodeIDBKey(index_key, &index_key_raw);
{
IndexedDBBackingStore::Transaction transaction1(backing_store());
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store()->PutRecord(
&transaction1, high_database_id, high_object_store_id, key1,
&value1, &record);
EXPECT_TRUE(s.ok());
s = backing_store()->PutIndexDataForRecord(
&transaction1, high_database_id, high_object_store_id,
invalid_high_index_id, index_key, record);
EXPECT_FALSE(s.ok());
s = backing_store()->PutIndexDataForRecord(
&transaction1, high_database_id, high_object_store_id,
high_index_id, index_key, record);
EXPECT_TRUE(s.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
}
{
IndexedDBBackingStore::Transaction transaction2(backing_store());
transaction2.Begin();
IndexedDBValue result_value;
leveldb::Status s = backing_store()->GetRecord(
&transaction2, high_database_id, high_object_store_id, key1,
&result_value);
EXPECT_TRUE(s.ok());
EXPECT_EQ(value1.bits, result_value.bits);
std::unique_ptr<IndexedDBKey> new_primary_key;
s = backing_store()->GetPrimaryKeyViaIndex(
&transaction2, high_database_id, high_object_store_id,
invalid_high_index_id, index_key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store()->GetPrimaryKeyViaIndex(
&transaction2, high_database_id, high_object_store_id,
high_index_id, index_key, &new_primary_key);
EXPECT_TRUE(s.ok());
EXPECT_TRUE(new_primary_key->Equals(key1));
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
}
loop.Quit();
}));
loop.Run();
}
// Make sure that other invalid ids do not crash.
TEST_F(IndexedDBBackingStoreTest, InvalidIds) {
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, IndexedDBKey key,
IndexedDBValue value) {
// valid ids for use when testing invalid ids
const int64_t database_id = 1;
const int64_t object_store_id = 1;
const int64_t index_id = kMinimumIndexId;
// index_ids must be > kMinimumIndexId
const int64_t invalid_low_index_id = 19;
IndexedDBValue result_value;
IndexedDBBackingStore::Transaction transaction1(backing_store);
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store->PutRecord(
&transaction1, database_id, KeyPrefix::kInvalidId, key, &value,
&record);
EXPECT_FALSE(s.ok());
s = backing_store->PutRecord(&transaction1, database_id, 0, key,
&value, &record);
EXPECT_FALSE(s.ok());
s = backing_store->PutRecord(&transaction1, KeyPrefix::kInvalidId,
object_store_id, key, &value, &record);
EXPECT_FALSE(s.ok());
s = backing_store->PutRecord(&transaction1, 0, object_store_id, key,
&value, &record);
EXPECT_FALSE(s.ok());
s = backing_store->GetRecord(&transaction1, database_id,
KeyPrefix::kInvalidId, key,
&result_value);
EXPECT_FALSE(s.ok());
s = backing_store->GetRecord(&transaction1, database_id, 0, key,
&result_value);
EXPECT_FALSE(s.ok());
s = backing_store->GetRecord(&transaction1, KeyPrefix::kInvalidId,
object_store_id, key, &result_value);
EXPECT_FALSE(s.ok());
s = backing_store->GetRecord(&transaction1, 0, object_store_id, key,
&result_value);
EXPECT_FALSE(s.ok());
std::unique_ptr<IndexedDBKey> new_primary_key;
s = backing_store->GetPrimaryKeyViaIndex(
&transaction1, database_id, object_store_id,
KeyPrefix::kInvalidId, key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store->GetPrimaryKeyViaIndex(
&transaction1, database_id, object_store_id,
invalid_low_index_id, key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store->GetPrimaryKeyViaIndex(&transaction1, database_id,
object_store_id, 0, key,
&new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store->GetPrimaryKeyViaIndex(
&transaction1, KeyPrefix::kInvalidId, object_store_id, index_id,
key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store->GetPrimaryKeyViaIndex(
&transaction1, database_id, KeyPrefix::kInvalidId, index_id,
key, &new_primary_key);
EXPECT_FALSE(s.ok());
},
base::Unretained(backing_store()), key1_, value1_));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const IndexedDBKey key = key1_;
IndexedDBValue value = value1_;
// valid ids for use when testing invalid ids
const int64_t database_id = 1;
const int64_t object_store_id = 1;
const int64_t index_id = kMinimumIndexId;
// index_ids must be > kMinimumIndexId
const int64_t invalid_low_index_id = 19;
IndexedDBValue result_value;
IndexedDBBackingStore::Transaction transaction1(backing_store());
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store()->PutRecord(
&transaction1, database_id, KeyPrefix::kInvalidId, key, &value,
&record);
EXPECT_FALSE(s.ok());
s = backing_store()->PutRecord(&transaction1, database_id, 0, key,
&value, &record);
EXPECT_FALSE(s.ok());
s = backing_store()->PutRecord(&transaction1, KeyPrefix::kInvalidId,
object_store_id, key, &value, &record);
EXPECT_FALSE(s.ok());
s = backing_store()->PutRecord(&transaction1, 0, object_store_id, key,
&value, &record);
EXPECT_FALSE(s.ok());
s = backing_store()->GetRecord(&transaction1, database_id,
KeyPrefix::kInvalidId, key,
&result_value);
EXPECT_FALSE(s.ok());
s = backing_store()->GetRecord(&transaction1, database_id, 0, key,
&result_value);
EXPECT_FALSE(s.ok());
s = backing_store()->GetRecord(&transaction1, KeyPrefix::kInvalidId,
object_store_id, key, &result_value);
EXPECT_FALSE(s.ok());
s = backing_store()->GetRecord(&transaction1, 0, object_store_id, key,
&result_value);
EXPECT_FALSE(s.ok());
std::unique_ptr<IndexedDBKey> new_primary_key;
s = backing_store()->GetPrimaryKeyViaIndex(
&transaction1, database_id, object_store_id, KeyPrefix::kInvalidId,
key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store()->GetPrimaryKeyViaIndex(
&transaction1, database_id, object_store_id, invalid_low_index_id,
key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store()->GetPrimaryKeyViaIndex(&transaction1, database_id,
object_store_id, 0, key,
&new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store()->GetPrimaryKeyViaIndex(
&transaction1, KeyPrefix::kInvalidId, object_store_id, index_id,
key, &new_primary_key);
EXPECT_FALSE(s.ok());
s = backing_store()->GetPrimaryKeyViaIndex(
&transaction1, database_id, KeyPrefix::kInvalidId, index_id, key,
&new_primary_key);
EXPECT_FALSE(s.ok());
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBBackingStoreTest, CreateDatabase) {
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store) {
const base::string16 database_name(ASCIIToUTF16("db1"));
int64_t database_id;
const int64_t version = 9;
const int64_t object_store_id = 99;
const base::string16 object_store_name(
ASCIIToUTF16("object_store1"));
const bool auto_increment = true;
const IndexedDBKeyPath object_store_key_path(
ASCIIToUTF16("object_store_key"));
const int64_t index_id = 999;
const base::string16 index_name(ASCIIToUTF16("index1"));
const bool unique = true;
const bool multi_entry = true;
const IndexedDBKeyPath index_key_path(ASCIIToUTF16("index_key"));
IndexedDBMetadataCoding metadata_coding;
{
IndexedDBDatabaseMetadata database;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store->db(), backing_store->origin_identifier(),
database_name, version, &database);
EXPECT_TRUE(s.ok());
EXPECT_GT(database.id, 0);
database_id = database.id;
IndexedDBBackingStore::Transaction transaction(backing_store);
transaction.Begin();
IndexedDBObjectStoreMetadata object_store;
s = metadata_coding.CreateObjectStore(
transaction.transaction(), database.id, object_store_id,
object_store_name, object_store_key_path, auto_increment,
&object_store);
EXPECT_TRUE(s.ok());
IndexedDBIndexMetadata index;
s = metadata_coding.CreateIndex(
transaction.transaction(), database.id, object_store.id,
index_id, index_name, index_key_path, unique, multi_entry,
&index);
EXPECT_TRUE(s.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
}
{
IndexedDBDatabaseMetadata database;
bool found;
leveldb::Status s = metadata_coding.ReadMetadataForDatabaseName(
backing_store->db(), backing_store->origin_identifier(),
database_name, &database, &found);
EXPECT_TRUE(s.ok());
EXPECT_TRUE(found);
// database.name is not filled in by the implementation.
EXPECT_EQ(version, database.version);
EXPECT_EQ(database_id, database.id);
EXPECT_EQ(1UL, database.object_stores.size());
IndexedDBObjectStoreMetadata object_store =
database.object_stores[object_store_id];
EXPECT_EQ(object_store_name, object_store.name);
EXPECT_EQ(object_store_key_path, object_store.key_path);
EXPECT_EQ(auto_increment, object_store.auto_increment);
EXPECT_EQ(1UL, object_store.indexes.size());
IndexedDBIndexMetadata index = object_store.indexes[index_id];
EXPECT_EQ(index_name, index.name);
EXPECT_EQ(index_key_path, index.key_path);
EXPECT_EQ(unique, index.unique);
EXPECT_EQ(multi_entry, index.multi_entry);
}
},
base::Unretained(backing_store())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const base::string16 database_name(ASCIIToUTF16("db1"));
int64_t database_id;
const int64_t version = 9;
const int64_t object_store_id = 99;
const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
const bool auto_increment = true;
const IndexedDBKeyPath object_store_key_path(
ASCIIToUTF16("object_store_key"));
const int64_t index_id = 999;
const base::string16 index_name(ASCIIToUTF16("index1"));
const bool unique = true;
const bool multi_entry = true;
const IndexedDBKeyPath index_key_path(ASCIIToUTF16("index_key"));
IndexedDBMetadataCoding metadata_coding;
{
IndexedDBDatabaseMetadata database;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store()->db(), backing_store()->origin_identifier(),
database_name, version, &database);
EXPECT_TRUE(s.ok());
EXPECT_GT(database.id, 0);
database_id = database.id;
IndexedDBBackingStore::Transaction transaction(backing_store());
transaction.Begin();
IndexedDBObjectStoreMetadata object_store;
s = metadata_coding.CreateObjectStore(
transaction.transaction(), database.id, object_store_id,
object_store_name, object_store_key_path, auto_increment,
&object_store);
EXPECT_TRUE(s.ok());
IndexedDBIndexMetadata index;
s = metadata_coding.CreateIndex(
transaction.transaction(), database.id, object_store.id, index_id,
index_name, index_key_path, unique, multi_entry, &index);
EXPECT_TRUE(s.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
}
{
IndexedDBDatabaseMetadata database;
bool found;
leveldb::Status s = metadata_coding.ReadMetadataForDatabaseName(
backing_store()->db(), backing_store()->origin_identifier(),
database_name, &database, &found);
EXPECT_TRUE(s.ok());
EXPECT_TRUE(found);
// database.name is not filled in by the implementation.
EXPECT_EQ(version, database.version);
EXPECT_EQ(database_id, database.id);
EXPECT_EQ(1UL, database.object_stores.size());
IndexedDBObjectStoreMetadata object_store =
database.object_stores[object_store_id];
EXPECT_EQ(object_store_name, object_store.name);
EXPECT_EQ(object_store_key_path, object_store.key_path);
EXPECT_EQ(auto_increment, object_store.auto_increment);
EXPECT_EQ(1UL, object_store.indexes.size());
IndexedDBIndexMetadata index = object_store.indexes[index_id];
EXPECT_EQ(index_name, index.name);
EXPECT_EQ(index_key_path, index.key_path);
EXPECT_EQ(unique, index.unique);
EXPECT_EQ(multi_entry, index.multi_entry);
}
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBBackingStoreTest, GetDatabaseNames) {
base::RunLoop loop;
idb_context_->TaskRunner()->PostTask(
FROM_HERE, base::BindOnce(
[](IndexedDBBackingStore* backing_store) {
const base::string16 db1_name(ASCIIToUTF16("db1"));
const int64_t db1_version = 1LL;
// Database records with DEFAULT_VERSION represent
// stale data, and should not be enumerated.
const base::string16 db2_name(ASCIIToUTF16("db2"));
const int64_t db2_version =
IndexedDBDatabaseMetadata::DEFAULT_VERSION;
IndexedDBMetadataCoding metadata_coding;
IndexedDBDatabaseMetadata db1;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store->db(),
backing_store->origin_identifier(), db1_name,
db1_version, &db1);
EXPECT_TRUE(s.ok());
EXPECT_GT(db1.id, 0LL);
IndexedDBDatabaseMetadata db2;
s = metadata_coding.CreateDatabase(
backing_store->db(),
backing_store->origin_identifier(), db2_name,
db2_version, &db2);
EXPECT_TRUE(s.ok());
EXPECT_GT(db2.id, db1.id);
std::vector<base::string16> names;
s = metadata_coding.ReadDatabaseNames(
backing_store->db(),
backing_store->origin_identifier(), &names);
EXPECT_TRUE(s.ok());
ASSERT_EQ(1U, names.size());
EXPECT_EQ(db1_name, names[0]);
},
base::Unretained(backing_store())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const base::string16 db1_name(ASCIIToUTF16("db1"));
const int64_t db1_version = 1LL;
// Database records with DEFAULT_VERSION represent
// stale data, and should not be enumerated.
const base::string16 db2_name(ASCIIToUTF16("db2"));
const int64_t db2_version = IndexedDBDatabaseMetadata::DEFAULT_VERSION;
IndexedDBMetadataCoding metadata_coding;
IndexedDBDatabaseMetadata db1;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store()->db(), backing_store()->origin_identifier(),
db1_name, db1_version, &db1);
EXPECT_TRUE(s.ok());
EXPECT_GT(db1.id, 0LL);
IndexedDBDatabaseMetadata db2;
s = metadata_coding.CreateDatabase(backing_store()->db(),
backing_store()->origin_identifier(),
db2_name, db2_version, &db2);
EXPECT_TRUE(s.ok());
EXPECT_GT(db2.id, db1.id);
std::vector<base::string16> names;
s = metadata_coding.ReadDatabaseNames(
backing_store()->db(), backing_store()->origin_identifier(),
&names);
EXPECT_TRUE(s.ok());
ASSERT_EQ(1U, names.size());
EXPECT_EQ(db1_name, names[0]);
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBBackingStoreTest, ReadCorruptionInfo) {
......@@ -1457,135 +1360,116 @@ TEST_F(IndexedDBBackingStoreTest, ReadCorruptionInfo) {
// keep the database.
// https://crbug.com/756447, https://crbug.com/829125, https://crbug.com/829141
TEST_F(IndexedDBBackingStoreTest, SchemaUpgradeWithoutBlobsSurvives) {
struct TestState {
int64_t database_id;
int64_t object_store_id = 99;
} state;
int64_t database_id;
const int64_t object_store_id = 99;
// The database metadata needs to be written so we can verify the blob entry
// keys are not detected.
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, TestState* state) {
const base::string16 database_name(ASCIIToUTF16("db1"));
const int64_t version = 9;
const base::string16 object_store_name(
ASCIIToUTF16("object_store1"));
const bool auto_increment = true;
const IndexedDBKeyPath object_store_key_path(
ASCIIToUTF16("object_store_key"));
IndexedDBMetadataCoding metadata_coding;
{
IndexedDBDatabaseMetadata database;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store->db(), backing_store->origin_identifier(),
database_name, version, &database);
EXPECT_TRUE(s.ok());
EXPECT_GT(database.id, 0);
state->database_id = database.id;
IndexedDBBackingStore::Transaction transaction(backing_store);
transaction.Begin();
IndexedDBObjectStoreMetadata object_store;
s = metadata_coding.CreateObjectStore(
transaction.transaction(), database.id,
state->object_store_id, object_store_name,
object_store_key_path, auto_increment, &object_store);
EXPECT_TRUE(s.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
}
},
base::Unretained(backing_store()), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
const base::string16 database_name(ASCIIToUTF16("db1"));
const int64_t version = 9;
const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
const bool auto_increment = true;
const IndexedDBKeyPath object_store_key_path(
ASCIIToUTF16("object_store_key"));
IndexedDBMetadataCoding metadata_coding;
{
IndexedDBDatabaseMetadata database;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store()->db(), backing_store()->origin_identifier(),
database_name, version, &database);
EXPECT_TRUE(s.ok());
EXPECT_GT(database.id, 0);
database_id = database.id;
IndexedDBBackingStore::Transaction transaction(backing_store());
transaction.Begin();
IndexedDBObjectStoreMetadata object_store;
s = metadata_coding.CreateObjectStore(
transaction.transaction(), database.id, object_store_id,
object_store_name, object_store_key_path, auto_increment,
&object_store);
EXPECT_TRUE(s.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
}
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, IndexedDBKey key,
IndexedDBValue value, TestState* state) {
// Save a value.
IndexedDBBackingStore::Transaction transaction1(backing_store);
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store->PutRecord(
&transaction1, state->database_id, state->object_store_id, key,
&value, &record);
EXPECT_TRUE(s.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
// Set the schema to 2, which was before blob support.
scoped_refptr<LevelDBTransaction> transaction =
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
backing_store->db());
const std::string schema_version_key = SchemaVersionKey::Encode();
indexed_db::PutInt(transaction.get(), schema_version_key, 2);
ASSERT_TRUE(transaction->Commit().ok());
},
base::Unretained(backing_store()), key1_, value1_,
base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
const IndexedDBKey key = key1_;
IndexedDBValue value = value1_;
// Save a value.
IndexedDBBackingStore::Transaction transaction1(backing_store());
transaction1.Begin();
IndexedDBBackingStore::RecordIdentifier record;
leveldb::Status s = backing_store()->PutRecord(
&transaction1, database_id, object_store_id, key, &value, &record);
EXPECT_TRUE(s.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction1.CommitPhaseTwo().ok());
// Set the schema to 2, which was before blob support.
auto transaction =
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
backing_store()->db());
const std::string schema_version_key = SchemaVersionKey::Encode();
indexed_db::PutInt(transaction.get(), schema_version_key, 2);
ASSERT_TRUE(transaction->Commit().ok());
}));
RunAllTasksUntilIdle();
DestroyFactoryAndBackingStore();
CreateFactoryAndBackingStore();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, IndexedDBKey key,
IndexedDBValue value, TestState* state) {
IndexedDBBackingStore::Transaction transaction2(backing_store);
transaction2.Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store
->GetRecord(&transaction2, state->database_id,
state->object_store_id, key,
&result_value)
.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(value.bits, result_value.bits);
// Test that we upgraded.
scoped_refptr<LevelDBTransaction> transaction =
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
backing_store->db());
const std::string schema_version_key = SchemaVersionKey::Encode();
int64_t found_int = 0;
bool found = false;
bool success =
indexed_db::GetInt(transaction.get(), schema_version_key,
&found_int, &found)
.ok();
ASSERT_TRUE(success);
ASSERT_TRUE(transaction->Commit().ok());
EXPECT_TRUE(found);
EXPECT_EQ(3, found_int);
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(backing_store()), key1_, value1_,
base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
const IndexedDBKey key = key1_;
IndexedDBValue value = value1_;
IndexedDBBackingStore::Transaction transaction2(backing_store());
transaction2.Begin();
IndexedDBValue result_value;
EXPECT_TRUE(backing_store()
->GetRecord(&transaction2, database_id, object_store_id,
key, &result_value)
.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction2.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction2.CommitPhaseTwo().ok());
EXPECT_EQ(value.bits, result_value.bits);
// Test that we upgraded.
auto transaction =
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
backing_store()->db());
const std::string schema_version_key = SchemaVersionKey::Encode();
int64_t found_int = 0;
bool found = false;
bool success = indexed_db::GetInt(transaction.get(), schema_version_key,
&found_int, &found)
.ok();
ASSERT_TRUE(success);
ASSERT_TRUE(transaction->Commit().ok());
EXPECT_TRUE(found);
EXPECT_EQ(3, found_int);
}));
RunAllTasksUntilIdle();
}
......@@ -1595,107 +1479,93 @@ TEST_F(IndexedDBBackingStoreTest, SchemaUpgradeWithoutBlobsSurvives) {
// treat it as corrupt and delete it.
// https://crbug.com/756447, https://crbug.com/829125, https://crbug.com/829141
TEST_F(IndexedDBBackingStoreTestWithBlobs, SchemaUpgradeWithBlobsCorrupt) {
struct TestState {
int64_t database_id;
int64_t object_store_id = 99;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3;
scoped_refptr<TestCallback> callback3;
} state;
int64_t database_id;
const int64_t object_store_id = 99;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction1;
scoped_refptr<TestCallback> callback1;
std::unique_ptr<IndexedDBBackingStore::Transaction> transaction3;
scoped_refptr<TestCallback> callback3;
// The database metadata needs to be written so the blob entry keys can
// be detected.
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStore* backing_store, TestState* state) {
const base::string16 database_name(ASCIIToUTF16("db1"));
const int64_t version = 9;
const base::string16 object_store_name(
ASCIIToUTF16("object_store1"));
const bool auto_increment = true;
const IndexedDBKeyPath object_store_key_path(
ASCIIToUTF16("object_store_key"));
IndexedDBMetadataCoding metadata_coding;
{
IndexedDBDatabaseMetadata database;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store->db(), backing_store->origin_identifier(),
database_name, version, &database);
EXPECT_TRUE(s.ok());
EXPECT_GT(database.id, 0);
state->database_id = database.id;
IndexedDBBackingStore::Transaction transaction(backing_store);
transaction.Begin();
IndexedDBObjectStoreMetadata object_store;
s = metadata_coding.CreateObjectStore(
transaction.transaction(), database.id,
state->object_store_id, object_store_name,
object_store_key_path, auto_increment, &object_store);
EXPECT_TRUE(s.ok());
scoped_refptr<TestCallback> callback(
base::MakeRefCounted<TestCallback>());
EXPECT_TRUE(transaction.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
}
},
base::Unretained(backing_store()), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
const base::string16 database_name(ASCIIToUTF16("db1"));
const int64_t version = 9;
const base::string16 object_store_name(ASCIIToUTF16("object_store1"));
const bool auto_increment = true;
const IndexedDBKeyPath object_store_key_path(
ASCIIToUTF16("object_store_key"));
IndexedDBMetadataCoding metadata_coding;
{
IndexedDBDatabaseMetadata database;
leveldb::Status s = metadata_coding.CreateDatabase(
backing_store()->db(), backing_store()->origin_identifier(),
database_name, version, &database);
EXPECT_TRUE(s.ok());
EXPECT_GT(database.id, 0);
database_id = database.id;
IndexedDBBackingStore::Transaction transaction(backing_store());
transaction.Begin();
IndexedDBObjectStoreMetadata object_store;
s = metadata_coding.CreateObjectStore(
transaction.transaction(), database.id, object_store_id,
object_store_name, object_store_key_path, auto_increment,
&object_store);
EXPECT_TRUE(s.ok());
auto callback = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction.CommitPhaseOne(callback).ok());
EXPECT_TRUE(callback->called);
EXPECT_TRUE(callback->succeeded);
EXPECT_TRUE(transaction.CommitPhaseTwo().ok());
}
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Initiate transaction1 - writing blobs.
state->transaction1 =
std::make_unique<IndexedDBBackingStore::Transaction>(
test->backing_store());
state->transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
EXPECT_TRUE(test->backing_store()
->PutRecord(state->transaction1.get(),
state->database_id,
state->object_store_id, test->key3_,
&test->value3_, &record)
.ok());
state->callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(
state->transaction1->CommitPhaseOne(state->callback1).ok());
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Initiate transaction1 - writing blobs.
transaction1 = std::make_unique<IndexedDBBackingStore::Transaction>(
backing_store());
transaction1->Begin();
IndexedDBBackingStore::RecordIdentifier record;
EXPECT_TRUE(backing_store()
->PutRecord(transaction1.get(), database_id,
object_store_id, key3_, &value3_, &record)
.ok());
callback1 = base::MakeRefCounted<TestCallback>();
EXPECT_TRUE(transaction1->CommitPhaseOne(callback1).ok());
}));
RunAllTasksUntilIdle();
idb_context_->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBBackingStoreTestWithBlobs* test, TestState* state) {
// Finish up transaction1, verifying blob writes.
EXPECT_TRUE(state->callback1->called);
EXPECT_TRUE(state->callback1->succeeded);
EXPECT_TRUE(test->CheckBlobWrites());
EXPECT_TRUE(state->transaction1->CommitPhaseTwo().ok());
// Set the schema to 2, which was before blob support.
scoped_refptr<LevelDBTransaction> transaction =
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
test->backing_store()->db());
const std::string schema_version_key = SchemaVersionKey::Encode();
indexed_db::PutInt(transaction.get(), schema_version_key, 2);
ASSERT_TRUE(transaction->Commit().ok());
// Clean up Transactions, etc on the IDB thread.
*state = TestState();
},
base::Unretained(this), base::Unretained(&state)));
FROM_HERE, base::BindLambdaForTesting([&]() {
// Finish up transaction1, verifying blob writes.
EXPECT_TRUE(callback1->called);
EXPECT_TRUE(callback1->succeeded);
EXPECT_TRUE(CheckBlobWrites());
EXPECT_TRUE(transaction1->CommitPhaseTwo().ok());
// Set the schema to 2, which was before blob support.
auto transaction =
IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
backing_store()->db());
const std::string schema_version_key = SchemaVersionKey::Encode();
indexed_db::PutInt(transaction.get(), schema_version_key, 2);
ASSERT_TRUE(transaction->Commit().ok());
// Clean up on the IDB sequence.
transaction1.reset();
callback1.reset();
transaction3.reset();
callback3.reset();
}));
RunAllTasksUntilIdle();
DestroyFactoryAndBackingStore();
......
......@@ -12,6 +12,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_clock.h"
#include "base/test/test_simple_task_runner.h"
......@@ -52,8 +53,8 @@ class MockIDBFactory : public IndexedDBFactoryImpl {
IndexedDBDataLossInfo data_loss_info;
bool disk_full;
leveldb::Status s;
scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore(
origin, data_directory, &data_loss_info, &disk_full, &s);
auto backing_store = OpenBackingStore(origin, data_directory,
&data_loss_info, &disk_full, &s);
EXPECT_EQ(blink::kWebIDBDataLossNone, data_loss_info.status);
return backing_store;
}
......@@ -84,7 +85,7 @@ class IndexedDBFactoryTest : public testing::Test {
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
context_ = base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir_.GetPath(), nullptr /* special_storage_policy */,
temp_dir_.GetPath(), /*special_storage_policy=*/nullptr,
quota_manager_proxy_.get());
}
......@@ -106,223 +107,210 @@ class IndexedDBFactoryTest : public testing::Test {
};
TEST_F(IndexedDBFactoryTest, BackingStoreLifetime) {
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
const Origin origin1 = Origin::Create(GURL("http://localhost:81"));
const Origin origin2 = Origin::Create(GURL("http://localhost:82"));
const Origin origin1 = Origin::Create(GURL("http://localhost:81"));
const Origin origin2 = Origin::Create(GURL("http://localhost:82"));
scoped_refptr<IndexedDBBackingStore> disk_store1 =
factory->TestOpenBackingStore(origin1, context->data_path());
auto disk_store1 =
factory->TestOpenBackingStore(origin1, context()->data_path());
scoped_refptr<IndexedDBBackingStore> disk_store2 =
factory->TestOpenBackingStore(origin1, context->data_path());
EXPECT_EQ(disk_store1.get(), disk_store2.get());
auto disk_store2 =
factory->TestOpenBackingStore(origin1, context()->data_path());
EXPECT_EQ(disk_store1.get(), disk_store2.get());
scoped_refptr<IndexedDBBackingStore> disk_store3 =
factory->TestOpenBackingStore(origin2, context->data_path());
auto disk_store3 =
factory->TestOpenBackingStore(origin2, context()->data_path());
factory->TestCloseBackingStore(disk_store1.get());
factory->TestCloseBackingStore(disk_store3.get());
factory->TestCloseBackingStore(disk_store1.get());
factory->TestCloseBackingStore(disk_store3.get());
EXPECT_FALSE(disk_store1->HasOneRef());
EXPECT_FALSE(disk_store2->HasOneRef());
EXPECT_TRUE(disk_store3->HasOneRef());
EXPECT_FALSE(disk_store1->HasOneRef());
EXPECT_FALSE(disk_store2->HasOneRef());
EXPECT_TRUE(disk_store3->HasOneRef());
disk_store2 = nullptr;
EXPECT_TRUE(disk_store1->HasOneRef());
},
base::Unretained(context())));
RunAllTasksUntilIdle();
disk_store2 = nullptr;
EXPECT_TRUE(disk_store1->HasOneRef());
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, BackingStoreLazyClose) {
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
scoped_refptr<IndexedDBBackingStore> store =
factory->TestOpenBackingStore(origin, context->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
factory->TestOpenBackingStore(origin, context->data_path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
// Take back a ref ptr and ensure that the actual close
// stops a running timer.
store = store_ptr;
factory->TestCloseBackingStore(store_ptr);
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
},
base::Unretained(context())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
const Origin origin = Origin::Create(GURL("http://localhost:81"));
auto store =
factory->TestOpenBackingStore(origin, context()->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
factory->TestOpenBackingStore(origin, context()->data_path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
// Take back a ref ptr and ensure that the actual close
// stops a running timer.
store = store_ptr;
factory->TestCloseBackingStore(store_ptr);
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, BackingStoreNoSweeping) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
{}, {kIDBTombstoneDeletion, kIDBTombstoneStatistics});
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context, &clock);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
scoped_refptr<IndexedDBBackingStore> store =
factory->TestOpenBackingStore(origin, context->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Reset the timer & stop the closing.
factory->TestOpenBackingStore(origin, context->data_path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
// Backing store should be totally closed.
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
store = factory->TestOpenBackingStore(origin, context->data_path());
store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
// Move the clock to start the next sweep.
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestGlobalSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
// Sweep should NOT be occurring.
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
// Backing store should be totally closed.
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
},
base::Unretained(context())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
auto factory = base::MakeRefCounted<MockIDBFactory>(context(), &clock);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
auto store =
factory->TestOpenBackingStore(origin, context()->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Reset the timer & stop the closing.
factory->TestOpenBackingStore(origin, context()->data_path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
// Backing store should be totally closed.
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
store = factory->TestOpenBackingStore(origin, context()->data_path());
store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
// Move the clock to start the next sweep.
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestGlobalSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
// Sweep should NOT be occurring.
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
// Backing store should be totally closed.
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, BackingStoreRunPreCloseTasks) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures({kIDBTombstoneStatistics},
{kIDBTombstoneDeletion});
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context, &clock);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
scoped_refptr<IndexedDBBackingStore> store =
factory->TestOpenBackingStore(origin, context->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Reset the timer & stop the closing.
factory->TestOpenBackingStore(origin, context->data_path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
// Backing store should be totally closed.
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
store = factory->TestOpenBackingStore(origin, context->data_path());
store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
// Move the clock to start the next sweep.
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestGlobalSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
// Sweep should be occuring.
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
ASSERT_NE(nullptr, store_ptr->pre_close_task_queue());
EXPECT_TRUE(store_ptr->pre_close_task_queue()->started());
// Stop sweep by opening a connection.
factory->TestOpenBackingStore(origin, context->data_path());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Move clock forward to trigger next sweep, but origin has longer
// sweep minimum, so nothing happens.
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestGlobalSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Reset, and move clock forward so the origin should allow a sweep.
factory->TestOpenBackingStore(origin, context->data_path());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestOriginSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
// Sweep should be occuring.
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
ASSERT_NE(nullptr, store_ptr->pre_close_task_queue());
EXPECT_TRUE(store_ptr->pre_close_task_queue()->started());
// Take back a ref ptr and ensure that the actual close
// stops a running timer.
store = store_ptr;
factory->TestCloseBackingStore(store_ptr);
},
base::Unretained(context())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
auto factory = base::MakeRefCounted<MockIDBFactory>(context(), &clock);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
auto store =
factory->TestOpenBackingStore(origin, context()->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Reset the timer & stop the closing.
factory->TestOpenBackingStore(origin, context()->data_path());
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
// Backing store should be totally closed.
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
store = factory->TestOpenBackingStore(origin, context()->data_path());
store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
// Move the clock to start the next sweep.
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestGlobalSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
// Sweep should be occuring.
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
ASSERT_NE(nullptr, store_ptr->pre_close_task_queue());
EXPECT_TRUE(store_ptr->pre_close_task_queue()->started());
// Stop sweep by opening a connection.
factory->TestOpenBackingStore(origin, context()->data_path());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Move clock forward to trigger next sweep, but origin has longer
// sweep minimum, so nothing happens.
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestGlobalSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
// Reset, and move clock forward so the origin should allow a sweep.
factory->TestOpenBackingStore(origin, context()->data_path());
EXPECT_EQ(nullptr, store_ptr->pre_close_task_queue());
clock.Advance(IndexedDBFactoryImpl::kMaxEarliestOriginSweepFromNow);
factory->TestReleaseBackingStore(store_ptr, false);
// Sweep should be occuring.
EXPECT_TRUE(store_ptr->close_timer()->IsRunning());
store_ptr->close_timer()->FireNow();
ASSERT_NE(nullptr, store_ptr->pre_close_task_queue());
EXPECT_TRUE(store_ptr->pre_close_task_queue()->started());
// Take back a ref ptr and ensure that the actual close
// stops a running timer.
store = store_ptr;
factory->TestCloseBackingStore(store_ptr);
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, BackingStoreCloseImmediatelySwitch) {
......@@ -331,109 +319,97 @@ TEST_F(IndexedDBFactoryTest, BackingStoreCloseImmediatelySwitch) {
{kIDBTombstoneDeletion});
base::CommandLine::ForCurrentProcess()->AppendSwitch(
kIDBCloseImmediatelySwitch);
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context, &clock);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
scoped_refptr<IndexedDBBackingStore> store =
factory->TestOpenBackingStore(origin, context->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
},
base::Unretained(context())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
base::SimpleTestClock clock;
clock.SetNow(base::Time::Now());
auto factory = base::MakeRefCounted<MockIDBFactory>(context(), &clock);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
auto store =
factory->TestOpenBackingStore(origin, context()->data_path());
// Give up the local refptr so that the factory has the only
// outstanding reference.
IndexedDBBackingStore* store_ptr = store.get();
store = nullptr;
EXPECT_FALSE(store_ptr->close_timer()->IsRunning());
factory->TestReleaseBackingStore(store_ptr, false);
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, MemoryBackingStoreLifetime) {
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
const Origin origin1 = Origin::Create(GURL("http://localhost:81"));
const Origin origin2 = Origin::Create(GURL("http://localhost:82"));
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
scoped_refptr<IndexedDBBackingStore> mem_store1 =
factory->TestOpenBackingStore(origin1, base::FilePath());
const Origin origin1 = Origin::Create(GURL("http://localhost:81"));
const Origin origin2 = Origin::Create(GURL("http://localhost:82"));
scoped_refptr<IndexedDBBackingStore> mem_store2 =
factory->TestOpenBackingStore(origin1, base::FilePath());
EXPECT_EQ(mem_store1.get(), mem_store2.get());
auto mem_store1 =
factory->TestOpenBackingStore(origin1, base::FilePath());
scoped_refptr<IndexedDBBackingStore> mem_store3 =
factory->TestOpenBackingStore(origin2, base::FilePath());
auto mem_store2 =
factory->TestOpenBackingStore(origin1, base::FilePath());
EXPECT_EQ(mem_store1.get(), mem_store2.get());
factory->TestCloseBackingStore(mem_store1.get());
factory->TestCloseBackingStore(mem_store3.get());
auto mem_store3 =
factory->TestOpenBackingStore(origin2, base::FilePath());
EXPECT_FALSE(mem_store1->HasOneRef());
EXPECT_FALSE(mem_store2->HasOneRef());
EXPECT_FALSE(mem_store3->HasOneRef());
factory->TestCloseBackingStore(mem_store1.get());
factory->TestCloseBackingStore(mem_store3.get());
factory = nullptr;
EXPECT_FALSE(mem_store1->HasOneRef()); // mem_store1 and 2
EXPECT_FALSE(mem_store2->HasOneRef()); // mem_store1 and 2
EXPECT_TRUE(mem_store3->HasOneRef());
EXPECT_FALSE(mem_store1->HasOneRef());
EXPECT_FALSE(mem_store2->HasOneRef());
EXPECT_FALSE(mem_store3->HasOneRef());
mem_store2 = nullptr;
EXPECT_TRUE(mem_store1->HasOneRef());
},
base::Unretained(context())));
factory = nullptr;
EXPECT_FALSE(mem_store1->HasOneRef()); // mem_store1 and 2
EXPECT_FALSE(mem_store2->HasOneRef()); // mem_store1 and 2
EXPECT_TRUE(mem_store3->HasOneRef());
RunAllTasksUntilIdle();
mem_store2 = nullptr;
EXPECT_TRUE(mem_store1->HasOneRef());
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, RejectLongOrigins) {
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context) {
base::FilePath temp_dir = context->data_path().DirName();
int limit = base::GetMaximumPathComponentLength(temp_dir);
EXPECT_GT(limit, 0);
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
std::string origin(limit + 1, 'x');
Origin too_long_origin =
Origin::Create(GURL("http://" + origin + ":81/"));
scoped_refptr<IndexedDBBackingStore> diskStore1 =
factory->TestOpenBackingStore(too_long_origin,
context->data_path());
EXPECT_FALSE(diskStore1.get());
Origin ok_origin =
Origin::Create(GURL("http://someorigin.com:82/"));
scoped_refptr<IndexedDBBackingStore> diskStore2 =
factory->TestOpenBackingStore(ok_origin, context->data_path());
EXPECT_TRUE(diskStore2.get());
// We need a manual close or Windows can't delete the temp
// directory.
factory->TestCloseBackingStore(diskStore2.get());
},
base::Unretained(context())));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
base::FilePath temp_dir = context()->data_path().DirName();
int limit = base::GetMaximumPathComponentLength(temp_dir);
EXPECT_GT(limit, 0);
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
std::string origin(limit + 1, 'x');
Origin too_long_origin =
Origin::Create(GURL("http://" + origin + ":81/"));
auto diskStore1 = factory->TestOpenBackingStore(too_long_origin,
context()->data_path());
EXPECT_FALSE(diskStore1.get());
Origin ok_origin = Origin::Create(GURL("http://someorigin.com:82/"));
auto diskStore2 =
factory->TestOpenBackingStore(ok_origin, context()->data_path());
EXPECT_TRUE(diskStore2.get());
// We need a manual close or Windows can't delete the temp
// directory.
factory->TestCloseBackingStore(diskStore2.get());
loop.Quit();
}));
loop.Run();
}
class DiskFullFactory : public IndexedDBFactoryImpl {
......@@ -479,227 +455,195 @@ class LookingForQuotaErrorMockCallbacks : public IndexedDBCallbacks {
};
TEST_F(IndexedDBFactoryTest, QuotaErrorOnDiskFull) {
scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks =
base::MakeRefCounted<LookingForQuotaErrorMockCallbacks>();
scoped_refptr<IndexedDBDatabaseCallbacks> dummy_database_callbacks =
auto callbacks = base::MakeRefCounted<LookingForQuotaErrorMockCallbacks>();
auto dummy_database_callbacks =
base::MakeRefCounted<IndexedDBDatabaseCallbacks>(nullptr, nullptr);
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<LookingForQuotaErrorMockCallbacks> callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks>
dummy_database_callbacks) {
const Origin origin = Origin::Create(GURL("http://localhost:81"));
scoped_refptr<DiskFullFactory> factory =
base::MakeRefCounted<DiskFullFactory>(context);
const base::string16 name(ASCIIToUTF16("name"));
std::unique_ptr<IndexedDBPendingConnection> connection(
std::make_unique<IndexedDBPendingConnection>(
callbacks, dummy_database_callbacks,
0 /* child_process_id */, 2 /* transaction_id */,
1 /* version */));
factory->Open(name, std::move(connection), origin,
context->data_path());
EXPECT_TRUE(callbacks->error_called());
},
base::Unretained(context()), std::move(callbacks),
std::move(dummy_database_callbacks)));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const Origin origin = Origin::Create(GURL("http://localhost:81"));
auto factory = base::MakeRefCounted<DiskFullFactory>(context());
const base::string16 name(ASCIIToUTF16("name"));
auto connection = std::make_unique<IndexedDBPendingConnection>(
callbacks, dummy_database_callbacks, /*child_process_id=*/0,
/*transaction_id=*/2, /*version=*/1);
factory->Open(name, std::move(connection), origin,
context()->data_path());
EXPECT_TRUE(callbacks->error_called());
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, BackingStoreReleasedOnForcedClose) {
auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIndexedDBCallbacks> callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
const int64_t transaction_id = 1;
std::unique_ptr<IndexedDBPendingConnection> connection(
std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, 0 /* child_process_id */,
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION));
factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
context->data_path());
EXPECT_TRUE(callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
callbacks->connection()->ForceClose();
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
},
base::Unretained(context()),
base::MakeRefCounted<MockIndexedDBCallbacks>(),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>()));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
const Origin origin = Origin::Create(GURL("http://localhost:81"));
const int64_t transaction_id = 1;
auto connection = std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION);
factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
context()->data_path());
EXPECT_TRUE(callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
callbacks->connection()->ForceClose();
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, BackingStoreReleaseDelayedOnClose) {
auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIndexedDBCallbacks> callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
const int64_t transaction_id = 1;
std::unique_ptr<IndexedDBPendingConnection> connection(
std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, 0 /* child_process_id */,
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION));
factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
context->data_path());
EXPECT_TRUE(callbacks->connection());
IndexedDBBackingStore* store =
callbacks->connection()->database()->backing_store();
EXPECT_FALSE(store->HasOneRef()); // Factory and database.
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
callbacks->connection()->Close();
EXPECT_TRUE(store->HasOneRef()); // Factory.
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
EXPECT_TRUE(store->close_timer()->IsRunning());
// Take a ref so it won't be destroyed out from under the test.
scoped_refptr<IndexedDBBackingStore> store_ref = store;
// Now simulate shutdown, which should stop the timer.
factory->ContextDestroyed();
EXPECT_TRUE(store->HasOneRef()); // Local.
EXPECT_FALSE(store->close_timer()->IsRunning());
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
},
base::Unretained(context()),
base::MakeRefCounted<MockIndexedDBCallbacks>(),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>()));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
const Origin origin = Origin::Create(GURL("http://localhost:81"));
const int64_t transaction_id = 1;
auto connection = std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION);
factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
context()->data_path());
EXPECT_TRUE(callbacks->connection());
IndexedDBBackingStore* store =
callbacks->connection()->database()->backing_store();
EXPECT_FALSE(store->HasOneRef()); // Factory and database.
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
callbacks->connection()->Close();
EXPECT_TRUE(store->HasOneRef()); // Factory.
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
EXPECT_TRUE(store->close_timer()->IsRunning());
// Take a ref so it won't be destroyed out from under the test.
scoped_refptr<IndexedDBBackingStore> store_ref = store;
// Now simulate shutdown, which should stop the timer.
factory->ContextDestroyed();
EXPECT_TRUE(store->HasOneRef()); // Local.
EXPECT_FALSE(store->close_timer()->IsRunning());
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, DeleteDatabaseClosesBackingStore) {
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIndexedDBCallbacks> callbacks) {
auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
/*expect_connection=*/false);
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
const Origin origin = Origin::Create(GURL("http://localhost:81"));
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
const Origin origin = Origin::Create(GURL("http://localhost:81"));
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
factory->DeleteDatabase(ASCIIToUTF16("db"), callbacks, origin,
context->data_path(),
false /* force_close */);
factory->DeleteDatabase(ASCIIToUTF16("db"), callbacks, origin,
context()->data_path(),
/*force_close=*/false);
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
// Now simulate shutdown, which should stop the timer.
factory->ContextDestroyed();
// Now simulate shutdown, which should stop the timer.
factory->ContextDestroyed();
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
},
base::Unretained(context()),
base::MakeRefCounted<MockIndexedDBCallbacks>(
false /*expect_connection*/)));
RunAllTasksUntilIdle();
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, GetDatabaseNamesClosesBackingStore) {
auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>(
/*expect_connection=*/false);
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIndexedDBCallbacks> callbacks) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
const Origin origin = Origin::Create(GURL("http://localhost:81"));
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
const Origin origin = Origin::Create(GURL("http://localhost:81"));
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
factory->GetDatabaseNames(callbacks, origin, context->data_path());
factory->GetDatabaseNames(callbacks, origin, context()->data_path());
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
// Now simulate shutdown, which should stop the timer.
factory->ContextDestroyed();
// Now simulate shutdown, which should stop the timer.
factory->ContextDestroyed();
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
},
base::Unretained(context()),
base::MakeRefCounted<MockIndexedDBCallbacks>(
false /*expect_connection*/)));
RunAllTasksUntilIdle();
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBFactoryTest, ForceCloseReleasesBackingStore) {
auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIndexedDBCallbacks> callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks) {
scoped_refptr<MockIDBFactory> factory =
base::MakeRefCounted<MockIDBFactory>(context);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
const int64_t transaction_id = 1;
std::unique_ptr<IndexedDBPendingConnection> connection(
std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, 0 /* child_process_id */,
transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION));
factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
context->data_path());
EXPECT_TRUE(callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
callbacks->connection()->Close();
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
FROM_HERE, base::BindLambdaForTesting([&]() {
auto factory = base::MakeRefCounted<MockIDBFactory>(context());
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
const Origin origin = Origin::Create(GURL("http://localhost:81"));
const int64_t transaction_id = 1;
auto connection = std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, /*child_process_id=*/0, transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION);
factory->Open(ASCIIToUTF16("db"), std::move(connection), origin,
context()->data_path());
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
EXPECT_TRUE(callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
// Ensure it is safe if the store is not open.
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
callbacks->connection()->Close();
EXPECT_TRUE(factory->IsBackingStoreOpen(origin));
EXPECT_TRUE(factory->IsBackingStorePendingClose(origin));
},
base::Unretained(context()),
base::MakeRefCounted<MockIndexedDBCallbacks>(),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>()));
RunAllTasksUntilIdle();
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
EXPECT_FALSE(factory->IsBackingStoreOpen(origin));
EXPECT_FALSE(factory->IsBackingStorePendingClose(origin));
// Ensure it is safe if the store is not open.
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
loop.Quit();
})),
loop.Run();
}
class UpgradeNeededCallbacks : public MockIndexedDBCallbacks {
......@@ -752,84 +696,64 @@ TEST_F(IndexedDBFactoryTest, DatabaseFailedOpen) {
// Created and used on IDB sequence.
scoped_refptr<MockIDBFactory> factory;
// Created on IO thread, used on IDB sequence.
scoped_refptr<UpgradeNeededCallbacks> upgrade_callbacks =
base::MakeRefCounted<UpgradeNeededCallbacks>();
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIDBFactory>* factory,
scoped_refptr<UpgradeNeededCallbacks>* upgrade_callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks,
const base::string16& db_name, int64_t transaction_id,
const Origin& origin) {
*factory = base::MakeRefCounted<MockIDBFactory>(context);
// Open at version 2.
const int64_t db_version = 2;
(*factory)->Open(
db_name,
std::make_unique<IndexedDBPendingConnection>(
*upgrade_callbacks, db_callbacks, 0 /* child_process_id */,
transaction_id, db_version),
origin, context->data_path());
EXPECT_TRUE((*factory)->IsDatabaseOpen(origin, db_name));
},
base::Unretained(context()), base::Unretained(&factory),
base::Unretained(&upgrade_callbacks),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>(), db_name,
transaction_id, origin));
// Pump the message loop so the upgrade transaction can run.
RunAllTasksUntilIdle();
// Created on IO thread, used on IDB sequence.
auto upgrade_callbacks = base::MakeRefCounted<UpgradeNeededCallbacks>();
auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
auto failed_open_callbacks = base::MakeRefCounted<ErrorCallbacks>();
auto db_callbacks2 = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIDBFactory> factory,
scoped_refptr<UpgradeNeededCallbacks> upgrade_callbacks,
scoped_refptr<ErrorCallbacks> failed_open_callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks,
const base::string16& db_name, int64_t transaction_id,
const Origin& origin) {
// Close the connection.
{
EXPECT_TRUE(upgrade_callbacks->connection());
upgrade_callbacks->connection()->database()->Commit(
upgrade_callbacks->connection()->GetTransaction(
transaction_id));
upgrade_callbacks->connection()->Close();
EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
}
// Open at version < 2, which will fail; ensure factory doesn't
// retain the database object.
{
const int64_t db_version = 1;
std::unique_ptr<IndexedDBPendingConnection> connection(
std::make_unique<IndexedDBPendingConnection>(
failed_open_callbacks, db_callbacks,
0 /* child_process_id */, transaction_id, db_version));
factory->Open(db_name, std::move(connection), origin,
context->data_path());
EXPECT_TRUE(failed_open_callbacks->saw_error());
EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
}
// Terminate all pending-close timers.
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
},
base::Unretained(context()), std::move(factory),
std::move(upgrade_callbacks), base::MakeRefCounted<ErrorCallbacks>(),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>(), db_name,
transaction_id, origin));
{
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
factory = base::MakeRefCounted<MockIDBFactory>(context());
// Open at version 2.
const int64_t db_version = 2;
factory->Open(db_name,
std::make_unique<IndexedDBPendingConnection>(
upgrade_callbacks, db_callbacks,
/*child_process_id=*/0, transaction_id, db_version),
origin, context()->data_path());
EXPECT_TRUE(factory->IsDatabaseOpen(origin, db_name));
loop.Quit();
}));
loop.Run();
}
RunAllTasksUntilIdle();
{
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
// Close the connection.
{
EXPECT_TRUE(upgrade_callbacks->connection());
upgrade_callbacks->connection()->database()->Commit(
upgrade_callbacks->connection()->GetTransaction(
transaction_id));
upgrade_callbacks->connection()->Close();
EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
}
// Open at version < 2, which will fail; ensure factory doesn't
// retain the database object.
{
const int64_t db_version = 1;
auto connection = std::make_unique<IndexedDBPendingConnection>(
failed_open_callbacks, db_callbacks2,
/*child_process_id=*/0, transaction_id, db_version);
factory->Open(db_name, std::move(connection), origin,
context()->data_path());
EXPECT_TRUE(failed_open_callbacks->saw_error());
EXPECT_FALSE(factory->IsDatabaseOpen(origin, db_name));
}
// Terminate all pending-close timers.
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
loop.Quit();
}));
loop.Run();
}
}
namespace {
......@@ -869,51 +793,43 @@ TEST_F(IndexedDBFactoryTest, DataFormatVersion) {
// Created and used on IDB sequence.
scoped_refptr<MockIDBFactory> factory;
// Created on IO thread, used on IDB sequence.
scoped_refptr<DataLossCallbacks> callbacks =
base::MakeRefCounted<DataLossCallbacks>();
auto callbacks = base::MakeRefCounted<DataLossCallbacks>();
const int64_t transaction_id = 1;
blink::WebIDBDataLoss result;
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* context,
scoped_refptr<MockIDBFactory>* factory,
scoped_refptr<DataLossCallbacks>* callbacks,
scoped_refptr<IndexedDBDatabaseCallbacks> db_callbacks,
const Origin& origin, int64_t transaction_id) {
*factory = base::MakeRefCounted<MockIDBFactory>(context);
(*factory)->Open(
ASCIIToUTF16("test_db"),
std::make_unique<IndexedDBPendingConnection>(
*callbacks, db_callbacks, 0 /* child_process_id */,
transaction_id, 1 /* version */),
origin, context->data_path());
},
base::Unretained(context()), base::Unretained(&factory),
base::Unretained(&callbacks),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>(), origin,
transaction_id));
RunAllTasksUntilIdle();
context()->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](scoped_refptr<MockIDBFactory> factory,
scoped_refptr<DataLossCallbacks> callbacks, const Origin& origin,
int64_t transaction_id, blink::WebIDBDataLoss* result) {
auto* connection = callbacks->connection();
EXPECT_TRUE(connection);
connection->database()->Commit(
connection->GetTransaction(transaction_id));
connection->Close();
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
*result = callbacks->data_loss();
},
std::move(factory), std::move(callbacks), origin, transaction_id,
base::Unretained(&result)));
RunAllTasksUntilIdle();
auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
{
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
factory = base::MakeRefCounted<MockIDBFactory>(context());
factory->Open(ASCIIToUTF16("test_db"),
std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, /*child_process_id=*/0,
transaction_id, /*version=*/1),
origin, context()->data_path());
loop.Quit();
}));
loop.Run();
}
{
base::RunLoop loop;
context()->TaskRunner()->PostTask(
FROM_HERE, base::BindLambdaForTesting([&]() {
auto* connection = callbacks->connection();
EXPECT_TRUE(connection);
connection->database()->Commit(
connection->GetTransaction(transaction_id));
connection->Close();
factory->ForceClose(origin, /*delete_in_memory_store=*/false);
result = callbacks->data_loss();
loop.Quit();
}));
loop.Run();
}
return result;
};
......
......@@ -12,6 +12,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
......@@ -79,11 +80,13 @@ class IndexedDBQuotaClientTest : public testing::Test {
const url::Origin& origin,
StorageType type) {
usage_ = -1;
client->GetOriginUsage(
origin, type,
base::BindOnce(&IndexedDBQuotaClientTest::OnGetOriginUsageComplete,
weak_factory_.GetWeakPtr()));
RunAllTasksUntilIdle();
base::RunLoop loop;
client->GetOriginUsage(origin, type,
base::BindLambdaForTesting([&](int64_t usage) {
usage_ = usage;
loop.Quit();
}));
loop.Run();
EXPECT_GT(usage_, -1);
return usage_;
}
......@@ -91,10 +94,14 @@ class IndexedDBQuotaClientTest : public testing::Test {
const std::set<url::Origin>& GetOriginsForType(storage::QuotaClient* client,
StorageType type) {
origins_.clear();
base::RunLoop loop;
client->GetOriginsForType(
type, base::BindOnce(&IndexedDBQuotaClientTest::OnGetOriginsComplete,
weak_factory_.GetWeakPtr()));
RunAllTasksUntilIdle();
type,
base::BindLambdaForTesting([&](const std::set<url::Origin>& origins) {
origins_ = origins;
loop.Quit();
}));
loop.Run();
return origins_;
}
......@@ -102,11 +109,14 @@ class IndexedDBQuotaClientTest : public testing::Test {
StorageType type,
const std::string& host) {
origins_.clear();
base::RunLoop loop;
client->GetOriginsForHost(
type, host,
base::BindOnce(&IndexedDBQuotaClientTest::OnGetOriginsComplete,
weak_factory_.GetWeakPtr()));
RunAllTasksUntilIdle();
base::BindLambdaForTesting([&](const std::set<url::Origin>& origins) {
origins_ = origins;
loop.Quit();
}));
loop.Run();
return origins_;
}
......@@ -114,11 +124,14 @@ class IndexedDBQuotaClientTest : public testing::Test {
const url::Origin& origin,
StorageType type) {
delete_status_ = blink::mojom::QuotaStatusCode::kUnknown;
base::RunLoop loop;
client->DeleteOriginData(
origin, type,
base::BindOnce(&IndexedDBQuotaClientTest::OnDeleteOriginComplete,
weak_factory_.GetWeakPtr()));
RunAllTasksUntilIdle();
base::BindLambdaForTesting([&](blink::mojom::QuotaStatusCode code) {
delete_status_ = code;
loop.Quit();
}));
loop.Run();
return delete_status_;
}
......@@ -142,16 +155,6 @@ class IndexedDBQuotaClientTest : public testing::Test {
}
private:
void OnGetOriginUsageComplete(int64_t usage) { usage_ = usage; }
void OnGetOriginsComplete(const std::set<url::Origin>& origins) {
origins_ = origins;
}
void OnDeleteOriginComplete(blink::mojom::QuotaStatusCode code) {
delete_status_ = code;
}
content::TestBrowserThreadBundle thread_bundle_;
base::ScopedTempDir temp_dir_;
int64_t usage_;
......
......@@ -9,6 +9,7 @@
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "content/browser/indexed_db/indexed_db_connection.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
......@@ -69,10 +70,9 @@ TEST_F(IndexedDBTest, ClearSessionOnlyDatabases) {
// Create the scope which will ensure we run the destructor of the context
// which should trigger the clean up.
{
scoped_refptr<IndexedDBContextImpl> idb_context =
base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
normal_path = idb_context->GetFilePathForTesting(kNormalOrigin);
session_only_path = idb_context->GetFilePathForTesting(kSessionOnlyOrigin);
......@@ -99,10 +99,9 @@ TEST_F(IndexedDBTest, SetForceKeepSessionState) {
{
// Create some indexedDB paths.
// With the levelDB backend, these are directories.
scoped_refptr<IndexedDBContextImpl> idb_context =
base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
// Save session state. This should bypass the destruction-time deletion.
idb_context->SetForceKeepSessionState();
......@@ -154,59 +153,56 @@ TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnDelete) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<IndexedDBContextImpl> idb_context =
base::MakeRefCounted<IndexedDBContextImpl>(temp_dir.GetPath(),
special_storage_policy_.get(),
quota_manager_proxy_.get());
auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
auto open_db_callbacks =
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
auto closed_db_callbacks =
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
auto open_callbacks =
base::MakeRefCounted<ForceCloseDBCallbacks>(idb_context, kTestOrigin);
auto closed_callbacks =
base::MakeRefCounted<ForceCloseDBCallbacks>(idb_context, kTestOrigin);
base::RunLoop loop;
idb_context->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* idb_context,
scoped_refptr<MockIndexedDBDatabaseCallbacks> open_db_callbacks,
scoped_refptr<MockIndexedDBDatabaseCallbacks> closed_db_callbacks,
scoped_refptr<ForceCloseDBCallbacks> open_callbacks,
scoped_refptr<ForceCloseDBCallbacks> closed_callbacks,
const Origin& origin) {
const int child_process_id = 0;
const int64_t host_transaction_id = 0;
const int64_t version = 0;
IndexedDBFactory* factory = idb_context->GetIDBFactory();
base::FilePath test_path =
idb_context->GetFilePathForTesting(origin);
factory->Open(base::ASCIIToUTF16("opendb"),
std::make_unique<IndexedDBPendingConnection>(
open_callbacks, open_db_callbacks,
child_process_id, host_transaction_id, version),
origin, idb_context->data_path());
EXPECT_TRUE(base::DirectoryExists(test_path));
factory->Open(base::ASCIIToUTF16("closeddb"),
std::make_unique<IndexedDBPendingConnection>(
closed_callbacks, closed_db_callbacks,
child_process_id, host_transaction_id, version),
origin, idb_context->data_path());
closed_callbacks->connection()->Close();
idb_context->DeleteForOrigin(origin);
EXPECT_TRUE(open_db_callbacks->forced_close_called());
EXPECT_FALSE(closed_db_callbacks->forced_close_called());
EXPECT_FALSE(base::DirectoryExists(test_path));
},
base::Unretained(idb_context.get()),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>(),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>(),
base::MakeRefCounted<ForceCloseDBCallbacks>(idb_context, kTestOrigin),
base::MakeRefCounted<ForceCloseDBCallbacks>(idb_context, kTestOrigin),
kTestOrigin));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const int child_process_id = 0;
const int64_t host_transaction_id = 0;
const int64_t version = 0;
IndexedDBFactory* factory = idb_context->GetIDBFactory();
base::FilePath test_path =
idb_context->GetFilePathForTesting(kTestOrigin);
factory->Open(base::ASCIIToUTF16("opendb"),
std::make_unique<IndexedDBPendingConnection>(
open_callbacks, open_db_callbacks, child_process_id,
host_transaction_id, version),
kTestOrigin, idb_context->data_path());
EXPECT_TRUE(base::DirectoryExists(test_path));
factory->Open(base::ASCIIToUTF16("closeddb"),
std::make_unique<IndexedDBPendingConnection>(
closed_callbacks, closed_db_callbacks,
child_process_id, host_transaction_id, version),
kTestOrigin, idb_context->data_path());
closed_callbacks->connection()->Close();
idb_context->DeleteForOrigin(kTestOrigin);
EXPECT_TRUE(open_db_callbacks->forced_close_called());
EXPECT_FALSE(closed_db_callbacks->forced_close_called());
EXPECT_FALSE(base::DirectoryExists(test_path));
loop.Quit();
}));
loop.Run();
}
TEST_F(IndexedDBTest, DeleteFailsIfDirectoryLocked) {
......@@ -214,24 +210,23 @@ TEST_F(IndexedDBTest, DeleteFailsIfDirectoryLocked) {
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
scoped_refptr<IndexedDBContextImpl> idb_context =
base::MakeRefCounted<IndexedDBContextImpl>(temp_dir.GetPath(),
special_storage_policy_.get(),
quota_manager_proxy_.get());
auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
base::FilePath test_path = idb_context->GetFilePathForTesting(kTestOrigin);
ASSERT_TRUE(base::CreateDirectory(test_path));
std::unique_ptr<LevelDBLock> lock =
LevelDBDatabase::LockForTesting(test_path);
auto lock = LevelDBDatabase::LockForTesting(test_path);
ASSERT_TRUE(lock);
// TODO(jsbell): Remove static_cast<> when overloads are eliminated.
void (IndexedDBContextImpl::* delete_for_origin)(const Origin&) =
&IndexedDBContextImpl::DeleteForOrigin;
base::RunLoop loop;
idb_context->TaskRunner()->PostTask(
FROM_HERE, base::BindOnce(delete_for_origin, idb_context, kTestOrigin));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
idb_context->DeleteForOrigin(kTestOrigin);
loop.Quit();
}));
loop.Run();
EXPECT_TRUE(base::DirectoryExists(test_path));
}
......@@ -240,54 +235,48 @@ TEST_F(IndexedDBTest, ForceCloseOpenDatabasesOnCommitFailure) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
scoped_refptr<IndexedDBContextImpl> idb_context =
base::MakeRefCounted<IndexedDBContextImpl>(temp_dir.GetPath(),
special_storage_policy_.get(),
quota_manager_proxy_.get());
auto idb_context = base::MakeRefCounted<IndexedDBContextImpl>(
temp_dir.GetPath(), special_storage_policy_.get(),
quota_manager_proxy_.get());
auto temp_path = temp_dir.GetPath();
auto callbacks = base::MakeRefCounted<MockIndexedDBCallbacks>();
auto db_callbacks = base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>();
base::RunLoop loop;
idb_context->TaskRunner()->PostTask(
FROM_HERE,
base::BindOnce(
[](IndexedDBContextImpl* idb_context, const base::FilePath temp_path,
scoped_refptr<MockIndexedDBCallbacks> callbacks,
scoped_refptr<MockIndexedDBDatabaseCallbacks> db_callbacks) {
const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
scoped_refptr<IndexedDBFactoryImpl> factory =
static_cast<IndexedDBFactoryImpl*>(
idb_context->GetIDBFactory());
const int child_process_id = 0;
const int64_t transaction_id = 1;
std::unique_ptr<IndexedDBPendingConnection> connection(
std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, child_process_id, transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION));
factory->Open(base::ASCIIToUTF16("db"), std::move(connection),
Origin(kTestOrigin), temp_path);
EXPECT_TRUE(callbacks->connection());
// ConnectionOpened() is usually called by the dispatcher.
idb_context->ConnectionOpened(kTestOrigin, callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(kTestOrigin));
// Simulate the write failure.
leveldb::Status status =
leveldb::Status::IOError("Simulated failure");
idb_context->GetIDBFactory()->HandleBackingStoreFailure(
kTestOrigin);
EXPECT_TRUE(db_callbacks->forced_close_called());
EXPECT_FALSE(factory->IsBackingStoreOpen(kTestOrigin));
},
base::Unretained(idb_context.get()), temp_dir.GetPath(),
base::MakeRefCounted<MockIndexedDBCallbacks>(),
base::MakeRefCounted<MockIndexedDBDatabaseCallbacks>()));
RunAllTasksUntilIdle();
FROM_HERE, base::BindLambdaForTesting([&]() {
const Origin kTestOrigin = Origin::Create(GURL("http://test/"));
auto* factory =
static_cast<IndexedDBFactoryImpl*>(idb_context->GetIDBFactory());
const int child_process_id = 0;
const int64_t transaction_id = 1;
auto connection = std::make_unique<IndexedDBPendingConnection>(
callbacks, db_callbacks, child_process_id, transaction_id,
IndexedDBDatabaseMetadata::DEFAULT_VERSION);
factory->Open(base::ASCIIToUTF16("db"), std::move(connection),
Origin(kTestOrigin), temp_path);
EXPECT_TRUE(callbacks->connection());
// ConnectionOpened() is usually called by the dispatcher.
idb_context->ConnectionOpened(kTestOrigin, callbacks->connection());
EXPECT_TRUE(factory->IsBackingStoreOpen(kTestOrigin));
// Simulate the write failure.
leveldb::Status status = leveldb::Status::IOError("Simulated failure");
factory->HandleBackingStoreFailure(kTestOrigin);
EXPECT_TRUE(db_callbacks->forced_close_called());
EXPECT_FALSE(factory->IsBackingStoreOpen(kTestOrigin));
loop.Quit();
}));
loop.Run();
}
} // namespace content
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment